Add property to junit xml pytest - python-3.x

I am trying to create a junit xml output file in pytest with custom attributes.
I searched for answers and found about xml_record_attribute and record_attribute.
def test(record_attribute):
record_attribute('index', '15')
...
At first I thought it would fit to my problem but then I realized it needs to be specified in each test.
I tried to do it with pytest_runtest_call hook so it would add attributes in each test run without the need to explicitly add the attributes in each test. But then it turned out that you can't use fixtures in hook (which makes sense).
Any idea how can I add an attribute to the junit xml output file without duplicating the code?
EDIT:
I have another idea of having a decorator which does that.
def xml_decorator(test):
def runner(xml_record_attribute):
xml_record_attribute('index', '15')
test()
reutrn runner
I am trying hook it with pytest_collection_modifyitems and decorate each test but it doesn't work.
def pytest_collection_modifyitems(session, config, items):
for item in items:
item.obj = xml_decorator(item.obj)

You could define a fixture that is automatically supplied to each test case:
import pytest
#pytest.fixture(autouse=True)
def record_index(record_xml_attribute):
record_xml_attribute('index', '15')
Note record_xml_attribute is not supported for xunit2, the default junit_family for pytest v6. To use record_xml_attribute with pytest v6, in pytest.ini set
[pytest]
junit_family = xunit1

Related

pytest- creating a plugin

I would like to create pytest plugin, that includes hook function and fixtures.
I have created it locally on a conftest.py file located at my root folder, and everything works fine. Now I would like to use this code in other projects, by converting the conftest.py code into a pytest- plugin.
In theory I know I simply need to create a package, however I have some really hard time understanding the entry points that I will use for this plugin.
I have an approximation of what I need, where my actual code goes inside the metadata of the test and reports it to a file.
The example code:
conftest.py
#pytest.fixture(scope = 'function')
def upload_manager():
def wrapper(val):
# Do something
return val
return wrapper
def pytest_report_teststatus(report):
if report.when == 'call':
# Do something
if report.when == 'setup':
# Do something
test_foo.py
def test_bar(upload_manager):
my_val = upload_manager(1000)
assert my_val == 1000
How would I go about using this code (fixture and hooks) as a package?
Okay, found an answer- This requires in the setup.py file to add the following key:
entry_points={
"pytest11": [
"name_of_plugin = package.file_name",
],
},
I've used it for a python plugin I wrote to extract data and metadata out of the tests themselves, it can be found here for future reference pytest-data-extractor.
If for some reason, someone needs it as package it can by installed directly from PyPi:
pip install pytest-data-extractor

Testing Flask microservices using unittest module

I have developed a very basic microservice using Flask framework.
A method in the application looks like this
#app.route('/add, methods=['POST'])
def add_info():
final = []
try:
info_obj.append(json.loads(request.data))
...
return jsonify(final)
Now I am attempting to write Unittest for this method and other method in this microservice. I am using import unittest to write my test.
Now here I am confused is how can I write tests to test the functionality of these http functions, which don't take regular argument of return regular results but rather fetch arguments from request data and return json based on that.
Is my approach correct? and if yes how can I test Microservices-like functionality using unittest module?
If you absolutely want unittesting, follow Patrick's guide here. But I suggest using PyTest. It's a breeze to get started. First you need a conftest.py. Then add your testfiles named test_... .py . The where your conftest is do $ pytest
Patrick has yet another PyTest + Flask guide here. You can view a demo of a conftest in a project here on how to set up db etc and a test file here

PyTest - Apply mock to all tests

I would like to apply a mock/patch that will apply to all tests, how can I do this?
I had tried putting it in a fixture, and using the fixture everywhere, but, the reapplication of the mock/patch on each test was leading to inconsistent id(my_mock) values.
You can make a fixture applied only once for all the test suite execution by scoping the fixture to 'session', and using it in all your tests:
import pytest
from unittest import mock
#pytest.fixture(scope='session', autouse=True)
def my_thing_mock():
with mock.patch.object(TheThingYouWantToMock, 'some_attribute') as _fixture:
yield _fixture

restart python (or reload modules) in py.test tests

I have a (python3) package that has completely different behaviour depending on how it's init()ed (perhaps not the best design, but rewriting is not an option). The module can only be init()ed once, a second time gives an error. I want to test this package (both behaviours) using py.test.
Note: the nature of the package makes the two behaviours mutually exclusive, there is no possible reason to ever want both in a singular program.
I have serveral test_xxx.py modules in my test directory. Each module will init the package in the way in needs (using fixtures). Since py.test starts the python interpreter once, running all test-modules in one py.test run fails.
Monkey-patching the package to allow a second init() is not something I want to do, since there is internal caching etc that might result in unexplained behaviour.
Is it possible to tell py.test to run each test module in a separate python process (thereby not being influenced by inits in another test-module)
Is there a way to reliably reload a package (including all sub-dependencies, etc)?
Is there another solution (I'm thinking of importing and then unimporting the package in a fixture, but this seems excessive)?
To reload a module, try using the reload() from library importlib
Example:
from importlib import reload
import some_lib
#do something
reload(some_lib)
Also, launching each test in a new process is viable, but multiprocessed code is kind of painful to debug.
Example
import some_test
from multiprocessing import Manager, Process
#create new return value holder, in this case a list
manager = Manager()
return_value = manager.list()
#create new process
process = Process(target=some_test.some_function, args=(arg, return_value))
#execute process
process.start()
#finish and return process
process.join()
#you can now use your return value as if it were a normal list,
#as long as it was assigned in your subprocess
Delete all your module imports and also your tests import that also import your modules:
import sys
for key in list(sys.modules.keys()):
if key.startswith("your_package_name") or key.startswith("test"):
del sys.modules[key]
you can use this as a fixture by configuring on your conftest.py file a fixture using the #pytest.fixture decorator.
Once I had similar problem, quite bad design though..
#pytest.fixture()
def module_type1():
mod = importlib.import_module('example')
mod._init(10)
yield mod
del sys.modules['example']
#pytest.fixture()
def module_type2():
mod = importlib.import_module('example')
mod._init(20)
yield mod
del sys.modules['example']
def test1(module_type1)
pass
def test2(module_type2)
pass
The example/init.py had something like this
def _init(val):
if 'sample' in globals():
logger.info(f'example already imported, val{sample}' )
else:
globals()['sample'] = val
logger.info(f'importing example with val : {val}')
output:
importing example with val : 10
importing example with val : 20
No clue as to how complex your package is, but if its just global variables, then this probably helps.
I have the same problem, and found three solutions:
reload(some_lib)
patch SUT, as the imported method is a key and value in SUT, you can patch the
SUT. Example, if you use f2 of m2 in m1, you can patch m1.f2 instead of m2.f2
import module, and use module.function.

Mocking print but allow using it in tests

Print can be mocked in the following way:
import unittest
import builtin
class TestSomething(unittest.TestCase):
#mock.patch('builtins.print')
def test_method(self, print_):
some_other_module.print_something()
However this means that in the python debug console (pydev debugger) and in the unit test method itself print cannot be used. This is rather inconvenient.
Is there a way to only mock the print method in some_other_module instead of in the testing module as well?
A way to sidestep this is to swap the use of print in the test module with some other function which just calls print, which I can do if there turns out to be no better solution.
#michele's "final solution" has an even cleaner alternative which works in my case:
from unittest import TestCase
from unittest.mock import patch
import module_under_test
class MyTestCase(TestCase):
#patch('module_under_test.print', create=True)
def test_something(self, print_):
module_under_test.print_something()
print_.assert_called_with("print something")
Yes you can! ... But just because you are using Python 3. In Python 3 print is a function and you can rewrite it without change the name. To understand the final solution I'll describe it step by step to have a final flexible and non intrusive solution.
Instrument Module
The trick is add at the top of your module that you will test a line like:
print = print
And now you can patch just print of your module. I wrote a test case where the mock_print_module.py is:
print = print
def print_something():
print("print something")
And the test module (I'm using autospec=True just to avoid errors like mock_print.asser_called_with):
from unittest import TestCase
from unittest.mock import patch
import mock_print_module
class MyTestCase(TestCase):
#patch("mock_print_module.print",autospec=True)
def test_something(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
I don't want to change my module but just patch print without lose functionalities
You can use patch on "builtins.print" without lose print functionality just by use side_effect patch's attribute:
#patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
Now you can trace your prints call without lose the logging and pydev debugger. The drawback of that approach is that you must fight against lot of noise to check your interested the print calls. Moreover you cannot chose what modules will be patched and what not.
Both modes don't work together
You cannot use both way together because if you use print=print in your module you save builtins.print in print variable at the load module time. Now when you patch builtins.print the module still use the original saved one.
If you would have a chance to use both you must wrap original print and not just record it. A way to implement it is use following instead of print=print:
import builtins
print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
The Final Solution
Do we really need to modify the original module to have a chance to patch all print calls in it? No, we can do it without change the module to test anyway. The only thing that we need is injecting a local print function in the module to override the builtins's one: we can do it in the test module instead of the the module to test. My example will become:
from unittest import TestCase
from unittest.mock import patch
import mock_print_module
import builtins
mock_print_module.print = lambda *args,**kwargs:builtins.print(*args,**kwargs)
class MyTestCase(TestCase):
#patch("mock_print_module.print",autospec=True)
def test_something(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
#patch("builtins.print",autospec=True,side_effect=print)
def test_somethingelse(self,mock_print):
mock_print_module.print_something()
mock_print.assert_called_with("print something")
and mock_print_module.py can be the clean original version with just:
def print_something():
print("print something")

Resources