Pytest Testing Idioms: Difference between revisions
(23 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
=External= | |||
=Internal= | =Internal= | ||
* [[Pytest#Subjects|Pytest]] | * [[Pytest#Subjects|Pytest]] | ||
=Overview= | =Overview= | ||
<font color=darkkhaki> | |||
Test files start with <code>test_</code> (?) | |||
</font> | |||
A function that starts with <code>test</code> is automatically considered a test to be executed. A function that does NOT start with <code>test</code> is ignored as test, so if one tries to specifically execute it, they get: | |||
<font size=-1> | |||
ERROR: not found: /Users/ovidiu/src/.../test_template.py::null_template_variables | |||
</font> | |||
<font color=darkkhaki> | |||
A test function can be injected with a test fixture: Available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory.</font> | |||
=Tested Code is Supposed to Throw Exception= | =Tested Code is Supposed to Throw Exception= | ||
Line 8: | Line 19: | ||
def test_something_that_should_throw_exception(): | def test_something_that_should_throw_exception(): | ||
with pytest.raises(Exception) as | with pytest.raises(Exception) as info: | ||
my_method() | |||
</syntaxhighlight> | |||
If the tested code raises a more specific exception, you can use that instead: | |||
<syntaxhighlight lang='python'> | |||
def test_something_that_should_throw_exception(): | |||
with pytest.raises(ValueError) as info: | |||
my_method() | my_method() | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This tests the exception arguments. ⚠️ In this case, it is very import to place the <code>assert</code> statements '''outside''' the <code>with</code> block, otherwise they will be silently ignored. | |||
<syntaxhighlight lang='python'> | |||
def test_something_that_should_throw_exception(): | |||
with pytest.raises(ValueError) as info: | |||
my_method() | |||
# important: interaction with 'info' must take place outside the with block | |||
assert info.value.args[0] == "some message" | |||
assert "some message" in str(info.value) | |||
</syntaxhighlight> | |||
Also see: {{Internal|Python_Language_Exceptions#Overview|Python Language | Exceptions}} | |||
=xunit-Style Setup= | |||
{{External|https://docs.pytest.org/en/7.1.x/how-to/xunit_setup.html}} | |||
<syntaxhighlight lang='python'> | |||
def setup_function(function): | |||
"""setup any state tied to the execution of the given function. | |||
Invoked for every test function in the module. | |||
'function' is optional. | |||
""" | |||
Path(Path(__file__).parent, './data/tmp').mkdir() | |||
def teardown_function(function): | |||
"""teardown any state that was previously setup with a setup_function | |||
call. | |||
'function' is optional. | |||
""" | |||
shutil.rmtree(str(Path(Path(__file__).parent, './data/tmp/'))) | |||
</syntaxhighlight> | |||
=Failing a Test= | |||
<syntaxhighlight lang='py'> | |||
from _pytest.outcomes import fail | |||
def test_something(): | |||
fail("this test failed because I wanted it to") | |||
</syntaxhighlight> | |||
=Mocking= | |||
{{Internal|Python_Mocking_with_unitest.mock|Mocking with <tt>unittest</tt>}} | |||
=Hierarchical Tests= | |||
<font color=darkkhaki> | <font color=darkkhaki> | ||
Find a solution for hierarchical tests for class hierarchies: the test hierarchy should match the class hierarchy and each layer in the hierarchy should test the behavior implemented by its corresponding class hierarchy level. This works well in Java and Junit. | |||
</font> | </font> | ||
=Skipping Tests in Python= | |||
<syntaxhighlight lang='py'> | |||
from unittest import skip | |||
@skip(reason="TODO 23jLP") | |||
def test_something(): | |||
[...] | |||
</syntaxhighlight> | |||
<syntaxhighlight lang='py'> | |||
@pytest.mark.skip(reason="TODO 23jLP") | |||
def test_something(): | |||
[...] | |||
</syntaxhighlight> |
Latest revision as of 22:48, 24 March 2023
External
Internal
Overview
Test files start with test_
(?)
A function that starts with test
is automatically considered a test to be executed. A function that does NOT start with test
is ignored as test, so if one tries to specifically execute it, they get:
ERROR: not found: /Users/ovidiu/src/.../test_template.py::null_template_variables
A test function can be injected with a test fixture: Available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory.
Tested Code is Supposed to Throw Exception
import pytest
def test_something_that_should_throw_exception():
with pytest.raises(Exception) as info:
my_method()
If the tested code raises a more specific exception, you can use that instead:
def test_something_that_should_throw_exception():
with pytest.raises(ValueError) as info:
my_method()
This tests the exception arguments. ⚠️ In this case, it is very import to place the assert
statements outside the with
block, otherwise they will be silently ignored.
def test_something_that_should_throw_exception():
with pytest.raises(ValueError) as info:
my_method()
# important: interaction with 'info' must take place outside the with block
assert info.value.args[0] == "some message"
assert "some message" in str(info.value)
Also see:
xunit-Style Setup
def setup_function(function):
"""setup any state tied to the execution of the given function.
Invoked for every test function in the module.
'function' is optional.
"""
Path(Path(__file__).parent, './data/tmp').mkdir()
def teardown_function(function):
"""teardown any state that was previously setup with a setup_function
call.
'function' is optional.
"""
shutil.rmtree(str(Path(Path(__file__).parent, './data/tmp/')))
Failing a Test
from _pytest.outcomes import fail
def test_something():
fail("this test failed because I wanted it to")
Mocking
Hierarchical Tests
Find a solution for hierarchical tests for class hierarchies: the test hierarchy should match the class hierarchy and each layer in the hierarchy should test the behavior implemented by its corresponding class hierarchy level. This works well in Java and Junit.
Skipping Tests in Python
from unittest import skip
@skip(reason="TODO 23jLP")
def test_something():
[...]
@pytest.mark.skip(reason="TODO 23jLP")
def test_something():
[...]