Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pytest where to store expected data

Tags:

python

pytest

Testing function I need to pass parameters and see the output matches the expected output.

It is easy when function's response is just a small array or a one-line string which can be defined inside the test function, but suppose function I test modifies a config file which can be huge. Or the resulting array is something 4 lines long if I define it explicitly. Where do I store that so my tests remain clean and easy to maintain?

Right now if that is string I just put a file near the .py test and do open() it inside the test:

def test_if_it_works():     with open('expected_asnwer_from_some_function.txt') as res_file:         expected_data = res_file.read()     input_data = ... # Maybe loaded from a file as well     assert expected_data == if_it_works(input_data) 

I see many problems with such approach, like the problem of maintaining this file up to date. It looks bad as well. I can make things probably better moving this to a fixture:

@pytest.fixture def expected_data()     with open('expected_asnwer_from_some_function.txt') as res_file:         expected_data = res_file.read()     return expected_data  @pytest.fixture def input_data()     return '1,2,3,4'  def test_if_it_works(input_data, expected_data):     assert expected_data == if_it_works(input_data) 

That just moves the problem to another place and usually I need to test if function works in case of empty input, input with a single item or multiple items, so I should create one big fixture including all three cases or multiple fixtures. In the end code gets quite messy.

If a function expects a complicated dictionary as an input or gives back the dictionary of the same huge size test code becomes ugly:

 @pytest.fixture  def input_data():      # It's just an example      return {['one_value': 3, 'one_value': 3, 'one_value': 3,      'anotherky': 3, 'somedata': 'somestring'],        ['login': 3, 'ip_address': 32, 'value': 53,        'one_value': 3], ['one_vae': 3, 'password': 13, 'lue': 3]} 

It's quite hard to read tests with such fixtures and keep them up to date.

Update

After searching a while I found a library which solved a part of a problem when instead of big config files I had large HTML responses. It's betamax.

For easier usage I created a fixture:

from betamax import Betamax  @pytest.fixture def session(request):     session = requests.Session()     recorder = Betamax(session)     recorder.use_cassette(os.path.join(os.path.dirname(__file__), 'fixtures', request.function.__name__)     recorder.start()     request.addfinalizer(recorder.stop)     return session 

So now in my tests I just use the session fixture and every request I make is being serialized automatically to the fixtures/test_name.json file so the next time I execute the test instead of doing a real HTTP request library loads it from the filesystem:

def test_if_response_is_ok(session):    r = session.get("http://google.com") 

It's quite handy because in order to keep these fixtures up to date I just need to clean the fixtures folder and rerun my tests.

like image 949
Glueon Avatar asked Apr 14 '15 12:04

Glueon


People also ask

Where do I put pytest files?

While the pytest discovery mechanism can find tests anywhere, pytests must be placed into separate directories from the product code packages. These directories may either be under the project root or under the Python package.

Are pytest fixtures cached?

Pytest only caches one instance of a fixture at a time, which means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope.

Why pytest is better than unittest?

Pytest has rich inbuilt features which require less piece of code compared to unittest. In the case of unittest, we have to import a module, create a class and then define testing functions inside the class. But in the case of pytest, we have to define the functions and assert the conditions inside them.

DO IT companies use pytest?

Who uses pytest? 90 companies reportedly use pytest in their tech stacks, including Zé Delivery, Back-end, and BestDoctor.


Video Answer


1 Answers

I had a similar problem once, where I have to test configuration file against an expected file. That's how I fixed it:

  1. Create a folder with the same name of your test module and at the same location. Put all your expected files inside that folder.

    test_foo/     expected_config_1.ini     expected_config_2.ini test_foo.py 
  2. Create a fixture responsible for moving the contents of this folder to a temporary file. I did use of tmpdir fixture for this matter.

    from __future__ import unicode_literals from distutils import dir_util from pytest import fixture import os   @fixture def datadir(tmpdir, request):     '''     Fixture responsible for searching a folder with the same name of test     module and, if available, moving all contents to a temporary directory so     tests can use them freely.     '''     filename = request.module.__file__     test_dir, _ = os.path.splitext(filename)      if os.path.isdir(test_dir):         dir_util.copy_tree(test_dir, bytes(tmpdir))      return tmpdir 

    Important: If you are using Python 3, replace dir_util.copy_tree(test_dir, bytes(tmpdir)) with dir_util.copy_tree(test_dir, str(tmpdir)).

  3. Use your new fixture.

    def test_foo(datadir):     expected_config_1 = datadir.join('expected_config_1.ini')     expected_config_2 = datadir.join('expected_config_2.ini') 

Remember: datadir is just the same as tmpdir fixture, plus the ability of working with your expected files placed into the a folder with the very name of test module.

like image 52
Fabio Menegazzo Avatar answered Sep 17 '22 11:09

Fabio Menegazzo