Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to let pytest rewrite assert in non-test modules

Tags:

python

pytest

We defined all our custom assertions in a separate python file which is not a test module.

For example: custom_asserts.py

class CustomAsserts(object):
    def silly_assert(self, foo, bar):
        assert foo == bar , 'some error message'

If we use assert directly in tests, we will get extra info about the AssertionError, which is very useful.

Output of directly use assert in tests:

>       assert 'foo' == 'bar', 'some error message'
E       AssertionError: some error message
E       assert 'foo' == 'bar'
E         - foo
E         + bar

But we found that if we call the assertion method we defined in separate module, extra info won't show.

from custom_asserts import CustomAsserts
asserts = CustomAsserts()
def test_silly():
    asserts.silly_assert('foo', 'bar')

Output after running the test:

>       assert 'foo' == 'bar', 'some error message'
E       AssertionError: some error message

And we also found this in pytest docs: Advanced assertion introspection

pytest only rewrites test modules directly discovered by its test collection process, so asserts in supporting modules which are not themselves test modules will not be rewritten.

So my question is, is there a way to let pytest do the same assert rewriting to other modules just like test modules? Or is there any hacky way to achieve that?

like image 934
Li Feng Avatar asked Jun 30 '16 22:06

Li Feng


1 Answers

Update:

Pytest 3.0 introduced a new method register_assert_rewrite to implement this exact feature. If you are using pytest 3.0 or later, please try this. register_assert_rewrite

Original answer:

It's kind of wired to answer my own question but I think I found the solution and want to share.

The trick is in how pytest collect test modules. We can define python_files in pytest.ini so that pytest will consider more modules as test modules.

For example, in my case, all my custom asserts module ends with 'asserts', so my pytest.ini is:

[pytest]
python_files = *asserts.py test_*.py *_test.py

Another tricky thing is in conftest.py. It seems we have to avoid import the asserts module in conftest.py. My assumption is that it looks like the technology pytest uses to rewrite assert is actually rewrite .pyc file, and since conftest.py is loaded before collecting, if we import the asserts module, .pyc of the module would be generated before collecting, which may make pytest unable to rewrite the .pyc file again.

So in my conftest.py, I have to do thing like:

@pytest.fixture(autouse=Ture)
def setup_asserts(request):
    from custom_asserts import CustomAsserts
    request.instance.asserts = CustomAsserts()

And I will get the extra error info just like using keyword assert directly in test script.

like image 112
Li Feng Avatar answered Sep 28 '22 17:09

Li Feng