I am trying to write my own implementation of test runner, what I struggle with is some kind of setUp and TearDown methods and how to override/invoke them.
class MetaTestCase(type):
def __call__(self, *args, **kwds):
return super().__call__(*args, **kwds)
def __new__(self, name, bases, attrs):
def replaced_fnc(fn):
def new_test(*args, **kwargs):
self.before(self)
result = fn(*args, **kwargs)
self.after(self)
return result
return new_test
for i in attrs:
if callable(attrs[i]):
attrs[i] = replaced_fnc(attrs[i])
return (super(MetaTestCase, self).__new__(self, name, bases, attrs))
def before(self):
print('Before')
def after(self):
print('After')
class TestCase(metaclass=MetaTestCase):
pass
class TestSuite(TestCase):
def before(self):
print('New Before')
def after(self):
print('New After')
def test(self):
print("Test")
if __name__ == "__main__":
TS = TestSuite()
TS.test()
The current output of that is:
Before
Test
After
But the expected output would be to override those functions from metaclass with those from Child Class like that:
New Before
Test
New After
But I don't really know metaclasses very good and how to pass Child methods to the Parent class.
CodePudding user response:
Like chepner suggest u don't need a metaclass for this. Define a test runner that takes the suite TS as an argument, and it just calls TS.before, TS.test, and TS.after explicitly and in sequence.
I just answer to question, how to call extra method from metaclass
Here u can read more aboute metaclasses. But quick answer will be, just use polymorphism.
So first of all u need to move your before and after method to parent class(not metaclass). In python when we work with objects, first argument is always self so we can use that like: args[0].before()
Next problem is infinite loop. We need to decied which function we need to overwrite in meta class, in my answaer I simple check if method name contain 'test', but u can create smth more sophisticated
class MetaTestCase(type):
def __call__(self, *args, **kwds):
return super().__call__(*args, **kwds)
def __new__(self, name, bases, attrs):
def replaced_fnc(fn):
def new_test(*args, **kwargs):
args[0].before()
result = fn(*args, **kwargs)
args[0].after()
return result
return new_test
for i in attrs:
if callable(attrs[i]) and 'test' in attrs[i].__name__:
attrs[i] = replaced_fnc(attrs[i])
return (super(MetaTestCase, self).__new__(self, name, bases, attrs))
class TestCase(metaclass=MetaTestCase):
def before(self):
print('Before')
def after(self):
print('After')
class TestSuite(TestCase):
def before(self):
print('New Before')
def after(self):
print('New After')
def test(self):
print("Test")
if __name__ == "__main__":
TS = TestSuite()
TS.test()