wigging
wigging

Reputation: 9180

Define input parameters for all pytest functions

I have a large Python project with many functions that are used by a single driver main.py. The driver provides the input parameters needed for all the functions. I'm using pytest to test the functions. A basic example of such a project is given below. In the example, parameters are defined as s, t, x, y, z, a, b, c, j, k.

# main.py
# ----------------------------------------------------------------------------

from funcA import funcA
from funcB import funcB

s = 3.4
t = 8
x = 12.9
y = 51
z = 1.04

a = 456.1
b = 203.09
c = 110.72
j = 2.89
k = 4.5

quantity, average = funcA(s, t, x, y, z)

weight = funcB(a, b, c, j, k, y, z)

print(f'{quantity=}')
print(f'{average=}')
print(f'{weight=}')

# funcA.py
# ----------------------------------------------------------------------------

def funcA(s, t, x, y, z):

    quantity = s + t
    average = (x + y + z) / 3

    return quantity, average

# funcB.py
# ----------------------------------------------------------------------------

def funcB(a, b, c, j, k, y, z):

    pounds = a + b + c
    kilograms = j + k + y + z
    weight = pounds + kilograms

    return weight

The tests for the example functions are shown below. The tests are located in a tests folder which is in the same directory as the main.py driver.

# test_funcA.py
# ----------------------------------------------------------------------------

import pytest
from funcA import funcA

def test_funcA():

    s = 3.4
    t = 8
    x = 12.9
    y = 51
    z = 1.04

    quantity, average = funcA(s, t, x, y, z)

    assert quantity == pytest.approx(11.4)
    assert average == pytest.approx(21.64, rel=1e-2)

# test_funcB.py
# ----------------------------------------------------------------------------

import pytest
from funcB import funcB

def test_funcB():

    a = 456.1
    b = 203.09
    c = 110.72
    j = 2.89
    k = 4.5

    y = 51
    z = 1.04

    weight = funcB(a, b, c, j, k, y, z)

    assert weight == pytest.approx(829.34)

For each test, I have to give the input parameters needed for the function being tested. This approach is cumbersome for a large project which has many parameters and functions. So instead of redefining the parameters for each test, I would like to define all the parameters once and pass those parameters to all the tests. How can I do that with pytest?

Approach 2

I defined a fixture in conftest.py which is located in the tests folder. This seems to be the pytest way to provide global variables (parameters) to all the tests. The tests use the fixture to get the input parameters for the function.

# conftest.py
# ----------------------------------------------------------------------------

import pytest
from dataclasses import dataclass

@dataclass
class Parameters:

    s = 3.4
    t = 8
    x = 12.9
    y = 51
    z = 1.04

    a = 456.1
    b = 203.09
    c = 110.72
    j = 2.89
    k = 4.5

@pytest.fixture()
def parameters():
    params = Parameters()
    return params

# test_funcA.py
# ----------------------------------------------------------------------------

import pytest
from funcA import funcA

def test_funcA(parameters):

    s = parameters.s
    t = parameters.t
    x = parameters.x
    y = parameters.y
    z = parameters.z

    quantity, average = funcA(s, t, x, y, z)

    assert quantity == pytest.approx(11.4)
    assert average == pytest.approx(21.64, rel=1e-2)

# test_funcB.py
# ----------------------------------------------------------------------------

import pytest
from funcB import funcB

def test_funcB(parameters):

    a = parameters.a
    b = parameters.b
    c = parameters.c
    j = parameters.j
    k = parameters.k
    y = parameters.y
    z = parameters.z

    weight = funcB(a, b, c, j, k, y, z)

    assert weight == pytest.approx(829.34)

This approach allows me to define the parameter values in one place (in the conftest.py file). But I still have to declare the input parameter variables in each test so they can be passed to the function that is being tested. Is there another approach where I don't have to redefine the parameters variables for each test? Could I just pass the needed variables to the test (see example below)?

import pytest
from funcA import funcA

def test_funcA(s, t, x, y, z):

    quantity, average = funcA(s, t, x, y, z)

    assert quantity == pytest.approx(11.4)
    assert average == pytest.approx(21.64, rel=1e-2)

Upvotes: 2

Views: 1018

Answers (1)

Michael Mintz
Michael Mintz

Reputation: 15526

If changes to data must be retained for each subsequent test, then you can pass data between tests using Module-scoped fixtures to solve multiple problems at once.

Here's what that might look like:

import pytest

@pytest.fixture(scope="module")
def data():
    a = 11
    b = 22
    c = 33
    d = 44
    yield [a, b, c, d]

def test_a(data):
    assert data[0] == 11
    data[1] = 2222

def test_ab(data):
    assert data[0] == 11
    assert data[1] == 2222

def test_abc(data):
    assert data[0] == 11
    assert data[1] == 2222
    assert data[2] == 33
    data[0] = 1111

def test_abd(data):
    assert data[0] == 1111
    assert data[1] == 2222
    assert data[3] == 44

Notice that the tests are changing data before the next test. Since the tests run alphabetically, they will all pass because the data is changed from the original set. For simplicity, I used a single fixture, but you can use as many fixtures as you'd like.

Copy/paste that into a Python file and try running it with pytest. It works because the data can be changed and passed between tests.


Part 2 (Based on your code, for what you're looking for.)

import pytest

@pytest.fixture(scope="module")
def s():
    value = 3.4
    yield value

@pytest.fixture(scope="module")
def t():
    value = 8
    yield value

@pytest.fixture(scope="module")
def x():
    value = 12.9
    yield value

@pytest.fixture(scope="module")
def y():
    value = 51
    yield value

@pytest.fixture(scope="module")
def z():
    value = 1.04
    yield value

def funcA(s, t, x, y, z):
    quantity = s + t
    average = (x + y + z) / 3
    return quantity, average

def test_funcA(s, t, x, y, z):
    quantity, average = funcA(s, t, x, y, z)
    assert quantity == pytest.approx(11.4)
    assert average == pytest.approx(21.64, rel=1e-2)

Upvotes: 1

Related Questions