When I have a parametrized pytest test like in the following case:
#parametrize('repetition', range(3))
#parametrize('name', ['test', '42'])
#parametrize('number', [3,7,54])
def test_example(repetition, name, number):
assert 1 = 1
the test runner prints out lines like follows:
tests\test_example.py:12: test_example[1-test-7]
where the parametrized values show up in the rectangular bracket next to the test functions name (test_example[0]). How can I access the content of the rectangular bracket (i.e. the string 0) inside the test? I have looked at the request
fixture, but could not find a suitable method for my needs.
To be clear: Inside the test method, I want to print the string 1-test-7, which pytest prints on the console.
How can I access the string 1-test-7, which pytest print out during a test?
I do not want to create this string by myself using something like
print str(repetition)+"-"+name+"-"+str(number)
since this would change every time I add a new parametrized argument to the test method.
In addition, if more complex objects are used in the parametrize list (like namedtuple), these objects are just references by a shortcut (e.g. object1, object2, ...).
Addendum: If I use the request fixture as an argument in my test method, I 'see' the string I would like to access when I use the following command
print request.keywords.node.__repr__()
which prints out something like
<Function 'test_example[2-test-3]'>
I am trying to find out how this method __repr__ is defined, in order to access directly the string test_example[2-test-3] from which I easily can extract the string I want, 2-test-3 in this example.
The solution makes use of the built-in request fixture which can be accessed as follows:
def test_example(repetition, name, number, request):
s = request.keywords.node.name
print s[s.find("[")+1:s.find("]")]
which will print the parameter string for each single parametrized test, so each parametrized test can be identified.
Related
I have a fixture (lets call it class_fixture) that dynamically returns a class I wish to run tests on.
I have another fixture (lets call it methods_fixture) that uses class_fixture and returns the names of all methods (that fit a specific criteria) of that class.
I also have a test that uses both fixtures and performs certain checks all methods:
def test_methods(class_fixture, methods_fixture):
for method in methods_fixture:
# DO TESTS ON class_fixture.method, for our example, lets test method name starts with DUMMY
assert getattr(class_fixture, method).__name__.startswith("DUMMY")
I would like to convert that test to be parameterized and be similar to the following:
def test(class_fixture, methods_fixture_as_parameter):
# This test function should generate multiple tests, one for each method returned by `methods_fixture`
# DO TESTS ON class_fixture.method, for our example, lets test method name starts with DUMMY
assert getattr(class_fixture, methods_fixture_as_parameter).__name__.startswith("DUMMY")
I tried going over pytest's parametrize documentation and couldn't find anything that fits. Since methods_fixture depends on a different fixture, I can't seem to implement what I want (because I assume I don't have the list of methods at test-creation time).
I couldn't get it to work with indirect, although it might be possible.
I also tried adding a pytest_generate_tests hook, but couldn't reach the values of methods_fixture or class_fixture to actually set the parameter values.
You can replace the fixtures with a function that iterate over the classes and their methods from parametrize decorator
def class_fixture_imp():
return []
#pytest.fixture
def class_fixture():
return class_fixture_imp()
def methods_data():
c = class_fixture_imp():
for f in methods_fixture(c):
yield c, f
#pytest.mark.parametrize('data', methods_data())
def test_something(data):
cl, method = data
assert getattr(cl, method).__name__.startswith("DUMMY")
Whenever pytest is outputting the tests that it has, it always prints out the parameter values, but never the names of the variables.
For example:
PASSED test_create_patient.py::test_create_patient[False-True]
What I would like it to output is:
PASSED test_create_patient.py::test_create_patient[arg1=False-arg2=True]
Is this possible? I have tried various pytest flags like -vvv but nothing seems to work for this. It would be very helpful for debugging purposes.
I've looked into answers like this https://stackoverflow.com/a/18496751/8903959 but I don't understand them.
Generally, you can use the ids parameter in pytest.mark.parametrize to customize the parameter ids. In your case, this is inconvenient. While ids allows you to provide a custom function to adapt the output, this function only gets the parameter value as argument, not the parameter name, so you cannot use it to compose your wanted output. Instead, you would have to provide a list of ids:
#pytest.mark.parametrize("arg1, arg2", [(False, True), (True, False)],
ids=["arg1=False-arg2=True",
"arg1=True-arg2=False"])
def test_create_patient(arg1, arg2):
pass
To avoid this, you can use the hook function pytest_make_parametrize_id instead, which gets both the parameter name and the value. You have to put it in a conftest.py visible to your test. In your case, you can write:
conftest.py
def pytest_make_parametrize_id(config, val, argname):
return f"{argname}={val}"
and you would get the wanted notation for each test.
If you want to have this notation only for verbose output (say, with the option -vv or more), you can use config.option to check the verbosity and adapt the id accordingly:
conftest.py
def pytest_make_parametrize_id(config, val, argname):
if config.option.verbose >= 2: # -vv or -vvv
return f"{argname}={val}"
return repr(val) # the default
I have a function which returns a list of values. The values are animals which are in the store.
[dog, cat, horse, ...]
Now the problem is I don't know the exact values inside the list. These will differ. How can I unittest this function?
This is what I have now but I don't know how / on which values to check?
From unittest import TestCase, mock
..
def test_get_in_house_animals(self, ):
value = self.trade.get_in_house_animals()
self.assertTrue(value)
Can someone explain me the ususal method to use mock data when you don't know the exact return values?
In a unit-test environment you mock all dependencies out of your unit.
In your case the e.g. database/repository/api/whatever instance must be mocked in your environment. In unittests it is allowed to test against specific implementation of your unit/boundary context. In this case the datasource. You can check in a test if you call
self.trade.get_in_house_animals()
that a specific function e.g. hasBeenCalled('findAll') which is responsible for the result in get_in_house_animals.
In your case mock the httpClient and assert that your api-enpoint "hasBeenCalled". The result of the endpoint is out of your unit and not part of your unittest.
Hope unit-testing made a bit clearer.
I am writing a python plugin for custom HTML report for pytest test results. I want to store some arbitrary test information (i.o. some python objects...) inside tests, and then when making report I want to reuse this information in the report. So far I have only came with a bit of hackish solution.
I pass request object to my test and fill the request.node._report_sections part of it with my data.
This object is then passed to TestReport.sections attribute, which is available via hook pytest_runtest_logreport, from which finally I can generate HTML and then I remove all my objects from sections attribute.
In pseudopythoncode:
def test_answer(request):
a = MyObject("Wooo")
request.node._report_sections.append(("call","myobj",a))
assert False
and
def pytest_runtest_logreport(report):
if report.when=="call":
#generate html from report.sections content
#clean report.sections list from MyObject objects
#(Which by the way contains 2-tuples, i.e. ("myobj",a))
Is there a better pytest way to do this?
This way seems OK.
Improvements I can suggest:
Think about using a fixture to create the MyObject object. Then you can place the request.node._report_sections.append(("call","myobj",a)) inside the fixture, and make it invisible in the test. Like this:
#pytest.fixture
def a(request):
a_ = MyObject("Wooo")
request.node._report_sections.append(("call","myobj",a_))
return a_
def test_answer(a):
...
Another idea, which is suitable in case you have this object in all of your tests, is to implement one of the hooks pytest_pycollect_makeitem or pytest_pyfunc_call, and "plant" the object there in the first place.
Writing some unit tests in python and using MagicMock to mock out a method that accepts a JSON string as input. In my unit test, I want to assert that it is called with given arguments, however I run into issues with the assert statement, since the ordering of objects within the dict doesn't matter, besides in the assert statement for the string. Simplified example of what I am trying to achieve below.
mock_funct = MagicMock()
# mocked function called elsewhere
expected = {"a":"a", "b":"b"}
mock_funct.assert_called_once_with(json.dumps(expected))
The above may pass or may fail due to the arbitrary ordering of the keys within the dict when it is dumped to json, ie both '{"a":"a", "b":"b"}' and '{"b":"b", "a":"a"}' are valid dumps but one would fail and one would pass, however I would like to write the test so that either would pass.
Unfortunately, you'll need to do your own checking here. You can get the calls from the mock via it's call_args_list attribute (or, simply call_args in this case since you have already asserted that it is called only once). I'll assume you're using unittest in my example code -- but it should be easy enough to adapt for any testing framework ...
mock_funct.assert_called_once_with(mock.ANY)
call = mock_funct.call_args
call_args, call_kwargs = call # calls are 2-tuples of (positional_args, keyword_args)
self.assertEqual(json.loads(call_args[0]), expected)
I've still used assert_called_once_with to make sure that the function was only called once with a single positional argument, but then I open up the call to look at that argument to check that it is correct.