2

I have my nice base test class which extends from the django test case and another class:

 class MyTestCase(TestCase, TestFreshProvisionedEachTest):

Now it all works nicely, except that to make it work we have to patch (with Foord's mock library) a couple of functions in the middleware.

My idea is that this would have worked fine:

@patch('spotlight.middleware.extract_host_name', new=lambda host_name:
TEST_DOMAIN)
@patch('spotlight.middleware.extract_host_key', new=lambda host_key:
company_name_to_db(TEST_DOMAIN))
class MyTestCase(TestCase, TestFreshProvisionedEachTest):

However it doesn't seem to work, the methods of the subclass are not patched. I thought then that there could be a way to do something like

MyTestCase = patch(MyTestCase, 'spotlight.middleware.extract_host_name', new=lambda host_name: TEST_DOMAIN)

But that also is not possible.

Is there a way to avoid this repetition and do a patch on a superclass which patches all the subclasses methods as well?

1 Answer 1

1

It's better to use a metaclass for that, as it makes easier to handle inheritance of test cases, than manually applying decorators. Something along these lines (I haven't tested it, but you should get the idea):

class TestMeta(type(TestCase)):
    def patch_method(cls, meth):
        raise NotImplementedError

    def __new__(mcls, name, bases, dct):
        to_patch = []

        for methname, meth in dct.items():
            if methname.startswith('test') and callable(meth):
                to_patch.append(methname)

        cls = super().__new__(mcls, name, bases, dct)

        for methname in to_patch:
            meth = getattr(cls, methname)
            meth = cls.patch_method(meth)
            setattr(cls, methname, meth)

        return cls

class PatchedTestCase(TestCase, metaclass=TestMeta):
    @classmethod
    def patch_method(cls, meth):
        meth = patch('spotlight.middleware.extract_host_name', 
                     new=lambda host_name: TEST_DOMAIN)(meth)

        meth = patch('spotlight.middleware.extract_host_key', 
                     new=lambda host_key: company_name_to_db(TEST_DOMAIN))(meth)

        return meth

class MyTestCase(PatchedTestCase, TestFreshProvisionedEachTest):
    ...

With this approach all methods of all subclasses of PatchedTestCase will be patched. You can also define other base classes with different patch_method implementations.

Sign up to request clarification or add additional context in comments.

3 Comments

That's very nice thank you, the only "problem" is that I'm redefining the logic of test discovery in the class that would already done by the patch decorator. Is there a way to still use patch on the class making it work for the subclasses as well?
I'm not sure I follow you... Can you explain in more details? The metaclass solution works essentially the same as class decorator.
Well I just mean that I thought there was a simpler solution, for example a way to use the patch class decorator as a function to redefine the class, but apparently it's not possible.. I'll give this metaclass a go then, thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.