Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining tests and passing an object from one test to another

Tags:

python

pytest

I'm trying to pass the result of one test to another in pytest - or more specifically, reuse an object created by the first test in the second test. This is how I currently do it.

@pytest.fixture(scope="module")
def result_holder:
    return []

def test_creation(result_holder):
    object = create_object()
    assert object.status == 'created' # test that creation works as expected
    result_holder.append(object.id) # I need this value for the next test

# ideally this test should only run if the previous test was successful
def test_deletion(result_holder):
    previous_id = result_holder.pop()
    object = get_object(previous_id) # here I retrieve the object created in the first test
    object.delete()
    assert object.status == 'deleted' # test for deletion

(before we go further, I'm aware of py.test passing results of one test to another - but the single answer on that question is off-topic, and the question itself is 2 years old)

Using fixtures like this doesn't feel super clean... And the behavior is not clear if the first test fails (although that can be remedied by testing for the content of the fixture, or using something like the incremental fixture in the pytest doc and the comments below). Is there a better/more canonical way to do this?

like image 467
pills Avatar asked Mar 12 '18 15:03

pills


People also ask

What is Autouse in pytest?

pytest is a very robust framework that comes with lots of features. One such feature is the autouse fixtures, a.k.a xUnit setup on steroids. They are a special type of fixture that gets invoked automatically, and its main use case is to act as a setup/teardown function.

What is Conftest PY in pytest?

conftest.py is where you setup test configurations and store the testcases that are used by test functions. The configurations and the testcases are called fixture in pytest. The test_*. py files are where the actual test functions reside.


2 Answers

For sharing data between tests, you could use the pytest namespace or cache.

Namespace

Example with sharing data via namespace. Declare the shared variable via hook in conftest.py:

# conftest.py

import pytest


def pytest_namespace():
    return {'shared': None}

Now access and redefine it in tests:

import pytest


def test_creation():
    pytest.shared = 'spam'
    assert True


def test_deletion():
    assert pytest.shared == 'spam'

Cache

The cache is a neat feature because it is persisted on disk between test runs, so usually it comes handy when reusing results of some long-running tasks to save time on repeated test runs, but you can also use it for sharing data between tests. The cache object is available via config. You can access it i.e. via request fixture:

def test_creation(request):
    request.config.cache.set('shared', 'spam')
    assert True


def test_deletion(request):
    assert request.config.cache.get('shared', None) == 'spam'

ideally this test should only run if the previous test was successful

There is a plugin for that: pytest-dependency. Example:

import pytest


@pytest.mark.dependency()
def test_creation():
    assert False


@pytest.mark.dependency(depends=['test_creation'])
def test_deletion():
    assert True

will yield:

$ pytest -v
============================= test session starts =============================
...
collected 2 items

test_spam.py::test_creation FAILED                                      [ 50%]
test_spam.py::test_deletion SKIPPED                                     [100%]

================================== FAILURES ===================================
________________________________ test_creation ________________________________

    def test_creation():
>       assert False
E       assert False

test_spam.py:5: AssertionError
===================== 1 failed, 1 skipped in 0.09 seconds =====================
like image 116
hoefling Avatar answered Oct 01 '22 21:10

hoefling


#Use return and then call it later so it'll look like: 

def test_creation():
    object = create_object()
    assert object.status == 'created'
    return(object.id) #this doesn't show on stdout but it will hand it to what's calling it


def test_update(id):
    object = test_creation
    object.id = id
    object.update()
    assert object.status == 'updated' # some more tests

#If this is what youre thinking of there ya go
like image 42
nAZklX Avatar answered Oct 01 '22 20:10

nAZklX