Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MRG] Refactor test_testing.py as per pytest design. #458

Merged
merged 2 commits into from
Dec 16, 2016

Conversation

kdexd
Copy link

@kdexd kdexd commented Dec 13, 2016

Third Phase PR on #411 (Succeeding PR #454)

This PR deals with refactor of tests in test_testing.py to adopt the pytest flavor.

Regex checking of exception messages is carried out by pytest_assert_raises.

@lesteve
Copy link
Member

lesteve commented Dec 13, 2016

I am in two minds about this I have to say ... I like assert_raises_regex because it forces you to test the error message. It is easier to get lazy with pytest.raises and forget to test the error message.

@kdexd
Copy link
Author

kdexd commented Dec 13, 2016

Well we can keep a general thumb rule as it was - we must match the description if we defined it ourselves. Default ones by python need not be tested. (Hence I left out OSError).

Or we can console ourselves by naming it pytest_raises_regex. I have been used to this with style since quite some time. Your call, feel free to choose other option and let it be as it is as well !

@lesteve
Copy link
Member

lesteve commented Dec 13, 2016

Hmm actually looking into this a bit more, it looks like assert_raises_regex is a bit broken with pytest if the error message is not matching. For some reason the tests are skipped in this case with a very long output talking about INTERNALERROR ...

from joblib.testing import assert_raises_regex


def raise_func():
    1/0


def test_unittest_wrong_message():
    assert_raises_regex(ZeroDivisionError, 'this will not match',
                        raise_func)

Output:

❯ pytest /tmp/test_simple.py
======================================================== test session starts ========================================================
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
rootdir: /tmp, inifile: 
plugins: cov-2.3.1
collected 1 items 

../../../../tmp/test_simple.py 
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/main.py", line 96, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/main.py", line 131, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 745, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 339, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 334, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 614, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/main.py", line 152, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 745, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 339, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 334, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 613, in execute
INTERNALERROR>     return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 254, in _wrapped_call
INTERNALERROR>     return call_outcome.get_result()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 265, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 614, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/runner.py", line 66, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/runner.py", line 79, in runtestprotocol
INTERNALERROR>     reports.append(call_and_report(item, "call", log))
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/runner.py", line 135, in call_and_report
INTERNALERROR>     report = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 745, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 339, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 334, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 613, in execute
INTERNALERROR>     return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 250, in _wrapped_call
INTERNALERROR>     wrap_controller.send(call_outcome)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/skipping.py", line 222, in pytest_runtest_makereport
INTERNALERROR>     rep = outcome.get_result()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 265, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py", line 614, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/runner.py", line 272, in pytest_runtest_makereport
INTERNALERROR>     longrepr = item.repr_failure(excinfo)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/python.py", line 587, in repr_failure
INTERNALERROR>     return self._repr_failure_py(excinfo, style=style)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/python.py", line 580, in _repr_failure_py
INTERNALERROR>     style=style)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/main.py", line 433, in _repr_failure_py
INTERNALERROR>     style=style, tbfilter=tbfilter)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/_code/code.py", line 423, in getrepr
INTERNALERROR>     return fmt.repr_excinfo(self)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/_code/code.py", line 635, in repr_excinfo
INTERNALERROR>     excinfo = ExceptionInfo((type(e), e, e.__traceback__))
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/_code/code.py", line 373, in __init__
INTERNALERROR>     self.traceback = _pytest._code.Traceback(self.tb, excinfo=self)
INTERNALERROR>   File "/home/lesteve/miniconda3/lib/python3.5/site-packages/_pytest/_code/code.py", line 270, in __init__
INTERNALERROR>     list.__init__(self, tb)
INTERNALERROR> TypeError: 'NoneType' object is not iterable

=================================================== no tests ran in 0.18 seconds ====================================================

All in all it may be worth googling a bit to see whether this problem has already been reported.

If this gives nothing we should move everywhere to pytest.raises + excinfo.match

@lesteve
Copy link
Member

lesteve commented Dec 13, 2016

OK I was actually using pytest 3.0.3 which has this problem. Turns out I was very unlucky 3.0.4 and 3.0.5 don't have this problem and neither has 2.9.2 ...

@lesteve
Copy link
Member

lesteve commented Dec 13, 2016

Here is a snippet to compare the error message when using assert_raises_regex compared to pytest.raises.

from joblib.testing import assert_raises_regex

import pytest


def raise_func(exception_class, message):
    # Some code 
    some_code = 1
    raise_func_inner(exception_class, message)
    # Some more code
    some_more_code = 2
    return 42


def raise_func_inner(exception_class, message):
    raise exception_class(message)


def test_pytest_wrong_exception():
    with pytest.raises(ValueError):
        raise_func(TypeError, 'bla')


def test_pytest_wrong_message():
    with pytest.raises(ValueError) as excinfo:
        raise_func(ValueError, 'bla')
    excinfo.match('boo')


def test_unittest_wrong_exception():
    assert_raises_regex(ValueError, 'bla',
                        raise_func, TypeError, 'bla')


def test_unittest_wrong_message():
    assert_raises_regex(ValueError, 'bla',
                        raise_func, ValueError, 'boo')

Output:

❯ pytest /tmp/test_pytest.py
======================================================== test session starts ========================================================
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
rootdir: /tmp, inifile: 
plugins: cov-2.3.1
collected 4 items 

../../../../tmp/test_pytest.py FFFF

============================================================= FAILURES ==============================================================
____________________________________________________ test_pytest_wrong_exception ____________________________________________________

    def test_pytest_wrong_exception():
        with pytest.raises(ValueError):
>           raise_func(TypeError, 'bla')

/tmp/test_pytest.py:21: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/tmp/test_pytest.py:9: in raise_func
    raise_func_inner(exception_class, message)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

exception_class = <class 'TypeError'>, message = 'bla'

    def raise_func_inner(exception_class, message):
>       raise exception_class(message)
E       TypeError: bla

/tmp/test_pytest.py:16: TypeError
_____________________________________________________ test_pytest_wrong_message _____________________________________________________

    def test_pytest_wrong_message():
        with pytest.raises(ValueError) as excinfo:
            raise_func(ValueError, 'bla')
>       excinfo.match('boo')
E       AssertionError: Pattern 'boo' not found in 'bla'

/tmp/test_pytest.py:27: AssertionError
___________________________________________________ test_unittest_wrong_exception ___________________________________________________

    def test_unittest_wrong_exception():
        assert_raises_regex(ValueError, 'bla',
>                           raise_func, TypeError, 'bla')

/tmp/test_pytest.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../miniconda3/lib/python3.5/unittest/case.py:1258: in assertRaisesRegex
    return context.handle('assertRaisesRegex', args, kwargs)
../../miniconda3/lib/python3.5/unittest/case.py:176: in handle
    callable_obj(*args, **kwargs)
/tmp/test_pytest.py:9: in raise_func
    raise_func_inner(exception_class, message)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def raise_func_inner(exception_class, message):
>       raise exception_class(message)
E       TypeError: bla

/tmp/test_pytest.py:16: TypeError
____________________________________________________ test_unittest_wrong_message ____________________________________________________
ValueError: boo

During handling of the above exception, another exception occurred:

    def test_unittest_wrong_message():
        assert_raises_regex(ValueError, 'bla',
>                           raise_func, ValueError, 'boo')

/tmp/test_pytest.py:37: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../miniconda3/lib/python3.5/unittest/case.py:1258: in assertRaisesRegex
    return context.handle('assertRaisesRegex', args, kwargs)
../../miniconda3/lib/python3.5/unittest/case.py:176: in handle
    callable_obj(*args, **kwargs)
../../miniconda3/lib/python3.5/unittest/case.py:212: in __exit__
    expected_regex.pattern, str(exc_value)))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unittest.case._AssertRaisesContext object at 0x7f4f9faeebe0>, standardMsg = '"bla" does not match "boo"'

    def _raiseFailure(self, standardMsg):
        msg = self.test_case._formatMessage(self.msg, standardMsg)
>       raise self.test_case.failureException(msg)
E       AssertionError: "bla" does not match "boo"

../../miniconda3/lib/python3.5/unittest/case.py:134: AssertionError
===================================================== 4 failed in 0.28 seconds ======================================================

As expected the pytest error message are a bit less noisy. I would be in favour of replacing all the assert_raises by pytest.assert_raises and assert_raises_regex by pytest.assert_raises + excinfo.match at one point. I don't think that is a high priority though.

@kdexd
Copy link
Author

kdexd commented Dec 13, 2016

OK I was actually using pytest 3.0.3 which has this problem. Turns out I was very unlucky 3.0.4 and 3.0.5 don't have this problem and neither has 2.9.2 ...

@lesteve Actually, lucky enough to catch this, as we have just specified pytest >= 3 and some other user might have complained in the future 😆

As expected the pytest error message are a bit less noisy. I would be in favour of replacing all the assert_raises by pytest.assert_raises and assert_raises_regex by pytest.assert_raises + excinfo.match at one point. I don't think that is a high priority though.

Sure. I will handle the already done modules at the end and take care about this for upcoming modules to process, including this one.

@kdexd
Copy link
Author

kdexd commented Dec 13, 2016

@lesteve Thanks for the informative code snippet and execution log, I should really learn very fine demonstration of code snippets from your comments 😄

@kdexd
Copy link
Author

kdexd commented Dec 16, 2016

@lesteve:
I am working on replacing all assert_raises and assert_raises_regex with pytest_assert_raises. This PR was made while we were following module wise approach. test_testing.py only had assert_raises / assert_raises_regex to be cleared out. We might want to merge this one so that I don't duplicate my work in a new PR.

@lesteve
Copy link
Member

lesteve commented Dec 16, 2016

LGTM, merging. Keep up the good work!

@lesteve lesteve merged commit 8c2e406 into joblib:master Dec 16, 2016
@lesteve
Copy link
Member

lesteve commented Dec 16, 2016

We might want to merge this one so that I don't duplicate my work in a new PR.

For the record, you don't have to duplicate your work. With git there are multiple ways to keep your work from a branch and have it available in another branch. One is git checkout -b another one is git cherry-pick.

@kdexd
Copy link
Author

kdexd commented Dec 16, 2016

I used to make one branch one another and rebase it later on master... But this cherry-pick looks interesting ! Thanks 😄

@kdexd kdexd deleted the pytestify-test-testing branch December 16, 2016 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants