Substitute a mock object for the metaclass of a class

Matt Wheeler m at
Mon Mar 13 18:36:06 EDT 2017

On Mon, 13 Mar 2017 at 00:52 Ben Finney <ben+python at> wrote:

> How can I override the metaclass of a Python class, with a
> `unittest.mock.MagicMock` instance instead?

At first I misunderstood what you were looking for, and was about to reply
to the effect of "you're too late, the metaclass has already been called so
this doesn't make sense", but I see that you're actually asking for
something that's a bit interesting...

> I have a function whose job involves working with the metaclass of an
> argument::
>     #
>     class Foo(object):
>         pass
>     def quux(existing_class):
>>         metaclass = type(existing_class)
>         new_class = metaclass(…)
> The unit tests for this function will need to assert that the calls to
> the metaclass go as expected, *without* actually calling a real class
> object.
> To write a unit test for this function, I want to temporarily override
> the metaclass of some classes in the system with a mock object instead.
> This will allow, for example, making assertions about how the metaclass
> was called, and ensuring no unwanted side effects.

The simple, lazy option would be to patch `type` to return your mocked
metaclass. i.e.

from unittest.mock import patch

import lorem

def test_things(mocktype):


Don't try and patch `type` from `builtins`. I did, and things go bad
because, unsurprisingly, `mock` calls `type` quite a lot internally :).

>     #
>     import unittest
>     import unittest.mock
>     import lorem
>     class stub_metaclass(type):
>         def __new__(metaclass, name, bases, namespace):
>             return super().__new__(metaclass, name, bases, namespace)
>     class quux_TestCase(unittest.TestCase):
>         @unittest.mock.patch.object(
>                     lorem.Foo, '__class__', side_effect=stub_metaclass)
>         def test_calls_expected_metaclass_with_class_name(
>                 self,
>                 mock_foo_metaclass,
>                 ):
>             lorem.quux(lorem.Foo)
>             mock_foo_metaclass.assert_called_with(
>                     'Foo', unittest.mock.ANY, unittest.mock.ANY)

When I try to add the stub_metaclass side_effect in to my code I get
`TypeError: __new__() missing 2 required positional arguments: 'bases' and
'namespace'` ... which seems quite reasonable, and I expect you're in a
better position to figure out how to handle that (the side effect may
actually need to be a wrapper around stub_metaclass which injects something

<rest of original snipped>

Matt Wheeler

More information about the Python-list mailing list