Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can one use the logging module in python with the unittest module?

I would like to use the python logging module to log all of the output from unittest so that I can incorporate it into a testing framework I am trying to write. The goal of this is to run the tests with 2 sets of output, one with simple output that tells the test case steps and a more debug level output so that when things go wrong we have as much info as possible. The output would be placed into two files, one that I could email out to people, and another kept in case of failures. I noticed that the TextTestRunner can use a stream, could this be used with the logging module? I'm planning on using some of the new features in python 2.7.

like image 896
user197674 Avatar asked Jul 27 '10 19:07

user197674


People also ask

What is unittest module in Python?

The unit test framework in Python is called unittest , which comes packaged with Python. Unit testing makes your code future proof since you anticipate the cases where your code could potentially fail or produce a bug. Though you cannot predict all of the cases, you still address most of them.

What does unittest main () do?

Internally, unittest. main() is using a few tricks to figure out the name of the module (source file) that contains the call to main() . It then imports this modules, examines it, gets a list of all classes and functions which could be tests (according the configuration) and then creates a test case for each of them.


1 Answers

You could, but I'm not sure it's your best approach.

For this approach, you would:

  1. Instantiate an in-memory stream that can be used by TextTestRunner. This is the sort of thing io.StringIO would be nearly perfect for, except that it works with Unicode input only, and I'm not sure that TextTestRunner writes Unicode properly to the stream. Your other option would be to code up your own in-memory stream that serves your purpose, perhaps wrapping StringIO with an encoder.

  2. Create your own TextTestRunner and initialize it with this in-memory stream.

  3. Create a class that reads from the stream and writes it to the log.

This could be as simple as:

class StreamLogger(object):
    def __init__(self, input_stream, output_logger):
        self.input_stream = input_stream
        self.output_logger
    def run(self):
        while True:
            line = input_stream.readline()
            if not line:
                break
            output_logger.error(line)

Problems with this approach:

  • You don't have much if any flexibility in directing different parts of TextTestRunner output to different log levels.
  • TextTestRunner, if misconfigured, will write a bunch of stuff that you probably don't want. The default verbosity is 1, which will write progress dots while you're testing... which will probably only get in your way in the logging output.
  • If you do this naïvely, you'll call stream_logger.run() only after you've written all your output to the stream. Thus, you won't get your logging output in real time, and your logging timestamps will be useless. You could solve this by spawning a separate thread for reading, for example, but then you'd need to choose/roll a stream that can handle a reader and writer thread working simultaneously, or fork a process and ditch the in-memory stream, or something relatively complicated.

The approach I'd suggest instead is to forego streams and simply roll your own test runner – called, say, LoggingTestRunner – that writes the test output to the logger in precisely the way you want it output. This will allow you to avoid all three of these problems.

like image 155
Owen S. Avatar answered Sep 28 '22 14:09

Owen S.