I did a new analyze of your code. The problem there is
- you define two streams for stdout and stderr. TextTextRunner uses only one stream: sys.stderr per default.
- TextTestRunner uses sys.stderr per default and this ist not your sys.stderr. That seems strange, see later. So your code works as not having defined any other output stream.
The situation changes when you call TextTextRunner() not with its defaults but if you pass explicitly your redefined sys.stderr. Now the output will be redirected to your file:
# does not at all what you want (writes output to stderr):
tests = unittest.TextTestRunner( descriptions=True, verbosity=2 ).run(suite)
# does not everything what you want (writes output to file only):
tests = unittest.TextTestRunner\
( stream=sys.stderr, descriptions=True, verbosity=2 ).run(suite)
One requirement is satisfied (write to a file) but the other one not. This is because only one of your defined streams is used (the one which you passed to TextTestRunner via stream). For the second requirement (write to console as well) there must be a way to write from one stream (the redirected stderr) into two output devices. This is in my opinion only possible if you change the your StreamToLogger.write()
method in an appropriate way:
def write( self, buf ) :
my_console_stream_instance.write(buf)
my_file_stream_instance.write(buf)
where my_console_stream_instance
is the stream which writes into the console and my_file_stream_instance
is the stream which writes into the file. So please look at the following example which should do what you want:
class StreamToLogger( object ):
def __init__( self ):
self.terminal = sys.stderr
self.log = open( "logger.log", "w" )
def write( self, buf ):
self.terminal.write(buf) # write to stderr
self.log.write(buf) # write to file
sys.stderr = StreamToLogger()
suite = suite()
tests = unittest.TextTestRunner\
( stream=sys.stderr, descriptions=True, verbosity=2 ).run( suite )
Now there are really two outputs: one to the screen and one into the file.
But why do you need to pass sys.stderr to the stream parameter of TextTestRunner? At the first glance it seems to be the same. But in The Python Language Reference in the chapter function definitions is the explanation:
Default parameter values are evaluated when the function definition is
executed. This means that the expression is evaluated once, when the
function is defined, and that the same “pre-computed” value is used
for each call. This is especially important to understand when a
default parameter is a mutable object.
The default sys.stderr of the stream parameter in TextTestRunner is evaluated before the execution of the stderr redefinition. This is why we must pass our redefinded sys.stderr anyhow. A little proof is: redefine sys.stderr before you import unittest: the code works well without passing the redefined sys.stderr.