С pytest, почему результаты одного теста отличаются от запуска всех тестов? - PullRequest
0 голосов
/ 26 июня 2018

Используя pytest с Python 3.6, я создаю симуляцию трубопровода для проекта. У меня есть тесты для работы с различными выравниваниями клапанов и тому подобным.

Странно то, что если я запускаю все тесты в каталоге, все тесты проходят. Если я пытаюсь запустить один тестовый класс, он терпит неудачу. Я попытался добавить методы setup / teardown, чтобы отделить его от остальной части кода, на случай, если другие классы каким-то образом на него повлияют.

Вот код теста ( ffc - библиотека компонентов, fff - библиотека методов):

class TestGate3TankLevels:
    @classmethod
    def setup_class(cls):
        ffc.tank2.level = 18.0
        fff.change_tank_level(ffc.tank2, ffc.tank2.level)
        fff.gate1_open()
        fff.gate2_open()
        fff.gate3_open()
        fff.gate4_open()

    def test_gate3_tank_levels(self):
        assert ffc.gate1.position == 100
        assert ffc.gate1.flow_out == 19542.86939891452
        assert ffc.gate1.press_out == 13.109851301499999

        assert ffc.gate2.position == 100
        assert ffc.gate2.flow_out == 19542.86939891452
        assert ffc.gate2.press_out == 6.5549256507499996

        assert ffc.gate3.position == 100
        assert ffc.gate3.flow_in == 19542.86939891452
        assert ffc.gate3.press_in == 13.109851301499999
        assert ffc.gate3.flow_out == 19542.86939891452
        assert ffc.gate3.press_out == 13.109851301499999
        assert ffc.gate6.flow_in == 19542.86939891452
        assert ffc.gate6.press_in == 13.109851301499999

        assert ffc.gate4.position == 100
        assert ffc.gate4.flow_in == 0.0
        assert ffc.gate4.press_in == 0.0
        assert ffc.gate4.flow_out == 0.0
        assert ffc.gate4.press_out == 0.0

    @classmethod
    def teardown_class(cls):
        ffc.tank2.level = 32.0
        fff.change_tank_level(ffc.tank2, ffc.tank2.level)

Когда запускается сам, я получаю следующие результаты:

F
test_fuel_components.py:408 (TestGate3TankLevels.test_gate3_tank_levels)
19542.86939891452 != 39085.73879782904

Expected :39085.73879782904
Actual   :19542.86939891452
 <Click to see difference>

self = <VirtualPLC.tests.models.fuel_farm.test_fuel_components.TestGate3TankLevels object at 0x7f9b35289a20>

    def test_gate3_tank_levels(self):
        assert ffc.gate1.position == 100
        assert ffc.gate1.flow_out == 19542.86939891452
        assert ffc.gate1.press_out == 13.109851301499999

        assert ffc.gate2.position == 100
        assert ffc.gate2.flow_out == 19542.86939891452
        assert ffc.gate2.press_out == 6.5549256507499996

        assert ffc.gate3.position == 100
        assert ffc.gate3.flow_in == 19542.86939891452
        assert ffc.gate3.press_in == 13.109851301499999
        assert ffc.gate3.flow_out == 19542.86939891452
        assert ffc.gate3.press_out == 13.109851301499999
>       assert ffc.gate6.flow_in == 19542.86939891452
E       assert 39085.73879782904 == 19542.86939891452
E        +  where 39085.73879782904 = <PipingSystems.valve.valve.Gate object at 0x7f9b3515ce80>.flow_in
E        +    where <PipingSystems.valve.valve.Gate object at 0x7f9b3515ce80> = ffc.gate6

test_fuel_components.py:423: AssertionError

Однако, если я запускаю все тесты в файле test_fuel_components.py, я все прохожу:

Testing started at 2:16 PM ...
/home/cody/PycharmProjects/VirtualPLC/venv/bin/python /home/cody/.local/share/JetBrains/Toolbox/apps/PyCharm-P/ch-0/182.3341.8/helpers/pycharm/_jb_pytest_runner.py --path /home/cody/PycharmProjects/VirtualPLC/tests/models/fuel_farm/test_fuel_components.py
Launching py.test with arguments /home/cody/PycharmProjects/VirtualPLC/tests/models/fuel_farm/test_fuel_components.py in /home/cody/PycharmProjects/VirtualPLC/tests/models/fuel_farm

============================= test session starts ==============================
platform linux -- Python 3.6.3, pytest-3.6.2, py-1.5.3, pluggy-0.6.0
rootdir: /home/cody/PycharmProjects/VirtualPLC/tests/models/fuel_farm, inifile:collected 20 items

test_fuel_components.py ....................                             [100%]

========================== 20 passed in 0.16 seconds ===========================
Process finished with exit code 0

Если я перенесу этот тестовый класс в другое место в файле (сейчас он находится в конце всех остальных тестов), это приведет к сбою любых последующих тестов, а также к самому провалу.

Я не понимаю, как тестовый класс может потерпеть неудачу сам по себе, но это нормально, когда он запускается с другими тестами.

Редактировать Ссылка на другие тесты (слишком долго для публикации здесь)

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Глобальное состояние резервуаров было проблемой. В начале у меня были тесты, которые меняли уровень одного из танков, но никогда не возвращали его в нормальное состояние.

Добавив некоторые вызовы функций до начала остальных тестов и убедившись, что уровни в резервуарах были «полными», он устранил все проблемы:

    fff.change_tank_level(ffc.tank1, 36)
    fff.change_tank_level(ffc.tank2, 36)

, где 36 - полный бак.

Итак, когда я попадаю в класс TestGate3TankLevels, изменение уровня бака работает корректно. Затем метод демонтажа возвращает уровни резервуара к норме.

0 голосов
/ 27 июня 2018

Таким образом, проблема в том, что состояние каждого бака и клапана инициализируется один раз, когда модели бака импортированы , как здесь бак1:

# Storage tanks
# Assumes 36 ft tall tank w/ 1 million gallon capacity = 27778 gallons per foot
# Assumes 16 inch diam transfer piping
tank1 = tank.Tank("Tank 1", level=36.0, fluid_density=DENSITY, spec_gravity=SPEC_GRAVITY, outlet_diam=16,
                  outlet_slope=0.25)
tank1.static_tank_press = tank1.level
tank1.gravity_flow(tank1.pipe_diam, tank1.pipe_slope, tank1.pipe_coeff)

Трудно предложить конкретную критику по этому проекту, не зная больше о том, что должен делать этот проект, но этот проект имеет огромное количество глобальных состояний, что делает тестирование чрезвычайно трудным.

Независимо от плюсов и минусов существующего проекта, вам необходимо сбросить состояние каждого резервуара до известного состояния в начале каждого теста, если вы хотите, чтобы тесты были надежными и изолированными. Учитывая, насколько глобальное состояние вовлечено здесь, ваши возможности довольно ограничены.

Моя рекомендация, если вы храните такое большое количество состояний в глобальном масштабе, - возвращаться к известному состоянию после каждого теста, используя методы setUp и tearDown.

Это не проверено, но вы ищете что-то вроде этого:

original_states = {}
tanks = [tank1, tank2, tank3, tank4, tank5, tank6]
tank_attrs = [ "name", "level", "fluid_density", "spec_grav",
               "tank_press", "flow_out", "pipe_diam", "pipe_slope" ]
def setUp(self):
    for tank in self.tanks:
        self.original_states[id(tank)] = {}
        for attr in self.tank_attrs:
            self.original_states[id(tank)] = getattr(tank, attr)

def tearDown(self):
    try:
        for tank in self.tanks:
            for attr, value in self.original_states[id(tank)].items():
                setattr(tank, attr, value)
    finally:
        self.original_states = {}

Который в основном сохраняет исходное состояние перед каждым тестом, а затем восстанавливает его после каждого теста. В некоторых из этих атрибутов есть какая-то магия, поэтому может потребоваться немного проб и ошибок, чтобы заставить его работать на 100%.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...