0

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.

Holloway
  • 6,412
  • 1
  • 26
  • 33
Alraku
  • 45
  • 1
  • 10
  • [This Link would help You](https://stackoverflow.com/questions/25062114/calling-child-class-method-from-parent-class-file-in-python) – Kedar U Shet Aug 31 '22 at 13:38
  • Does this answer your question? [calling child class method from parent class file in python](https://stackoverflow.com/questions/25062114/calling-child-class-method-from-parent-class-file-in-python) – Kedar U Shet Aug 31 '22 at 13:39
  • Thanks @KedarUShet but this misses the whole point of not executing explicitly the method name from Child class. That's why I have modified metaclass to change class behaviour (so it is automatically invoking before/after methods). – Alraku Aug 31 '22 at 13:47
  • You 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. – chepner Aug 31 '22 at 14:17

1 Answers1

0

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()
  • Thank you @Michał, I didn't think of this like that because every answer in the internet was about using "self" or "cls" to indicate to the method that we want to invoke. Completly forgot about that we have those args and kwargs passed to the replacing function. This is a correct answer. – Alraku Aug 31 '22 at 14:28
  • Could you please provide an example with replacing function out of the meta class? Because I have a pickling problem with multiprocessing. – Alraku Sep 01 '22 at 15:34