I would like to setup a new log file for every testcase. Something like the following:
import unittest
import logging
log = logging.getLogger()
class TestExample(unittest.TestCase):
def setUp(self):
next_testcase = ???
self.hdl = logging.FileHandler(
'testcase_%s.log' % next_testcase, mode='w')
log.addHandler(self.hdl)
def tearDown(self):
log.removeHandler(self.hdl)
How can I fi开发者_运维问答nd out the name testcase?
Or how would you implement something like the above?
Tricky! :-)
The problem here is that you can't know which function is the next one to be called - not without interfering in the unittest code directly.
However, if it where possible to change each test case into another method, that would call a "setup" custom made for that test case, and then execute the original test case, it would be solved.
In other words: one need to automate the change of each test case in your declared class into another function that: executes a personalized set-up, and them perfoms the original test. This should be done at class creation time.
It may seem complicated, but it is rather a job fit for "metaclasses" - as we are just doing what they are meant for: customizing the class creation.
So, by changing the metaclass for your TestCase to one that check each item on the class - if it is a test (i.e., if its name starts with "test"), creates a new wrapper function that does the above functionality, you can get it going.
(There is one further step needed: since the method name, and the (object for the) original test method must remain the same once created, one intermediate function has to be created to preserve the closure for each test case. Else, the variables method_name
and original_method
in the code bellow would all be set for the name and contents of the last function in the class at the end of the for
loop)
There you go:
# -*- coding: utf-8 -*-
import unittest
import logging
#template method:
def log_setup(self, name):
#self.hdl = logging.FileHandler('testcase_%s.log' % name, mode='w')
#log.addHandler(self.hdl)
self.name = name
class MetaTestCase(type):
def __new__(cls, cls_name, bases, dict):
for name, item in dict.items():
if name.startswith("test"):
def scope_freezer(method_name, original_method):
def wrapper(self, *args, **kw):
log_setup(self, method_name)
return original_method(self, *args, **kw)
return wrapper
dict[name] = scope_freezer(name, item)
return type.__new__(cls, cls_name, bases, dict)
class TestExample(unittest.TestCase):
__metaclass__ = MetaTestCase
def test_a(self):
print self.name
def test_b(self):
print self.name
def tearDown(self):
pass
#log.removeHandler(self.hdl)
unittest.main()
(to simplify testing on my side, I commented out the actual logging lines - the "prints" are working fine -- any code in the log_setup function will be run with self
and name
properly set.)
And of course, you can factor out this metclass in another module and just import it for use on your tests.
import unittest
import logging
def log_setup(self, name):
print name
class TestExample(unittest.TestCase):
def test_method_name(self):
print self.method_name
def __getattribute__(self, name):
safe_getattr = super(TestExample, self).__getattribute__
if name.startswith("test_"):
self.method_name = name
log_setup(self, name)
return safe_getattr(name)
if __name__ == "__main__":
unittest.main()
精彩评论