Py.test - Применение переменных к декораторам из CSV?

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

Краткое краткое изложение:

В настоящее время я разрабатываю набор тестов Selenium для набора веб-сайтов, которые по сути одинаковы по функциональности, используя py.test

  • Результаты тестов загружаются в TestRail с использованием плагина https://pypi.org/project/pytest-testrail/.

  • Тесты помечаются с помощью decorator @ pytestrail.case (id) с уникальным идентификатором дела

Типичный мой тест выглядит так:

@pytestrail.case('C100123')  # associates the function with the relevant TR case
@pytest.mark.usefixtures()
def test_login():
   # test code goes here

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

Я попробовал управляемый данными подход с csv и списком тестов и их идентификаторами в TestRail.

Пример:

website1.csv:
Case ID | Test name
C100123 | test_login


website2.csv:
Case ID | Test name
C222123 | test_login

Код, который я написал, будет использовать модуль inspect, чтобы найти имя выполняемого теста, найти соответствующий идентификатор теста и поместить его в переменную с именем test_id:

import csv
import inspect
class trp(object):
def __init__(self):
    pass


with open(testcsv) as f:  # testcsv could be website1.csv or website2.csv
    reader = csv.reader(f)
    next(reader)  # skip header
    tests = [r for r in reader]


def gettestcase(self):
    self.current_test = inspect.stack()[3][3]
    for row in trp.tests:
        if self.current_test == row[2]:
            self.test_id = (row[0])
            print(self.test_id)
            return self.test_id, self.current_test


def gettestid(self):
    self.gettestcase()

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

@pytestrail.case(test_id)  # now a variable
@pytest.mark.usefixtures()
def test_login():
   trp.gettestid()
   # test code goes here

Так что если бы я запустил test_login для website1, декоратор выглядел бы так:

@pytestrail.case('C100123')

и если бы я запустил test_login для website2, декоратор был бы:

@pytestrail.case('C222123')

Я очень гордился тем, что смог придумать это решение сам, и попробовал его... оно не сработало. Хотя код работает сам по себе, я бы получил исключение, потому что test_id не определен (я понимаю, почему - gettestcase выполняется после декоратора, поэтому, конечно, он потерпит крах.

Единственный способ справиться с этим - применить csv и testID перед выполнением любого тестового кода. У меня вопрос - откуда мне знать, как связать тесты с их идентификаторами? Каким было бы элегантное, минимальное решение для этого?

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

2 ответа

Решение

pytest очень хорошо выполняет все виды метапрограммирования для тестов. Если я правильно понимаю ваш вопрос, приведенный ниже код выполнит динамические тесты, помеченные pytestrail.case маркер. В корневом каталоге проекта создайте файл с именем conftest.py и поместите в него этот код:

import csv
from pytest_testrail.plugin import pytestrail


with open('website1.csv') as f:
    reader = csv.reader(f)
    next(reader)
    tests = [r for r in reader]


def pytest_collection_modifyitems(items):
    for item in items:
        for testid, testname in tests:
            if item.name == testname:
                item.add_marker(pytestrail.case(testid))

Теперь вам не нужно отмечать тест @pytestrail.case()на всех - просто напишите остаток кода и pytest позаботится о маркировке:

def test_login():
    assert True

когда pytest начинается, код выше будет читать website1.csv и сохраните идентификаторы и имена тестов так же, как вы делали это в своем коде. Перед началом тестового прогона, pytest_collection_modifyitems hook выполнится, проанализировав собранные тесты - если тест имеет то же имя, что и в CSV-файле, pytest добавит pytestrail.case маркер с идентификатором теста к нему.

Я полагаю, что причина, по которой это не работает, как вы ожидаете, связана с тем, как python читает и выполняет файлы. Когда python начинает выполняться, он считывает связанные файлы Python и выполняет каждую строку по очереди, по очереди. Для вещей на уровне корневого отступа (определения функций / классов, декораторы, назначения переменных и т. Д.) Это означает, что они запускаются ровно один раз при загрузке и никогда больше. В вашем случае интерпретатор python считывает в декораторе pytest-testrail, затем в декораторе pytest и, наконец, в определении функции, выполняя каждое из них раз и навсегда.

(Заметьте, именно поэтому вы никогда не должны использовать изменяемые объекты в качестве значений по умолчанию для аргументов функции: общие ошибки)

Учитывая, что вы хотите сначала определить текущее имя теста, затем связать его с идентификатором теста и, наконец, использовать этот идентификатор с декоратором, я не уверен, что это возможно с текущей функциональностью pytest-testrail. По крайней мере, невозможно без некоторого эзотерического и трудного для отладки / поддержки взлома с использованием дескрипторов или тому подобного.

Я думаю, что у вас есть один вариант: использовать другой клиент TestRail и обновить структуру pytest, чтобы использовать новый клиент. Два клиента Python, которые я могу порекомендовать, это testrail-python и TRAW (TestRail Api Wrapper)(*)

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

(*) полное раскрытие: я являюсь создателем / сопровождающим TRAW, а также внес значительный вклад в testrail-python

Другие вопросы по тегам