Всем привет, недавно познакомился с py.test как альтернатива для unittest. Очень понравился, плюс красивые отчетики в allure.
Вопрос собственно таков:
Как организовать фикстуру, которая глобально, каждому модулю/классу будет передавать активный инстанс webdriver через conftest.py и к нему нормально можно будет обратится
На офф документации нашел только обход метод с добавлением к классам атрибута, меня такой вариант не устраивает. Практически 20 классов в каждом по 10-15 тест методов, изменять устоявшиеся сигнатуры как то не охота. Плюс ко всему, проект разростается с огромной скоростью, и на данный момент все функционирует, только нету централизованного запуска.
А как у вас сейчас передается инстанс вебдрайвера?
А в чем проблема сделать реализацию управления инициализацией вебдрайвера (можно например по принципу singleton) через один какой-то класс в отдельном модуле и потом его импортировать в нужное место, где необходимо использовать инстанс вебдрайвера?
Или покажите хотя бы псевдокод, чтобы хотя приблизительно было понятно, как вы хотите получать инстанс вебдрайвера в идеальном случае.
Сейчас у меня фикстура лежит на уровне модуля (пережитки unittest и green).
В самой фикстуре, была обработка cmd (какой ключ, такой и браузер юзаем) и глобально (на уровне модуля) передача самого драйвера в каждый класс.
Что хочется:
Универсальный conftest.py который будет отдавать объект драйвера, в независимости от вложенности пакетом/модулей/классов самих тестов.
def setup_module():
"""Подготовка модуля и обработка CLI"""
global driver
driver = webdriver.Ie()
if '*ie' in sys.argv:
driver = webdriver.Ie()
elif '*chrome' in sys.argv:
driver = webdriver.Chrome()
elif '*firefox' in sys.argv:
driver = webdriver.Firefox()
def teardown_module():
driver.close()
сами тесты
class Test_Home_Page(Check):
""" Testing Home page"""
@classmethod
def setup_class(cls):
cls.driver = driver
cls.link = "http://localhost:8888/"
cls.driver.get(cls.link)
@allure.story('Ответа сервера')
def test_status_code(self):
assert(self.status_code())
@allure.story('Наличие head-тега страницы')
def test_header(self):
assert(self.header())
Пока, все эти классы в одном модуле, но нужно их разнести, плюс - будет множество других модулей/пакетов в скором времени.
Проблема скорее даже не в саомй передаче, а в том, что я не могу получить это екземпляр, так как все написанное в conftest есть фикстура, и вот как ее передавать в модуль тестов - мне не понятно.
Для чего вы группируете тесты по классам? Есть какое-либо еще действие, которое надо совершить перед тестами кроме инициализации драйвера?
Логически разделять тесты лучше по разным файлам.
Если нет - просто передайте имя фикстуры как параметр в тест.
то есть test_some_thing(driver_up_session):
а дальше можно использовать driver_up_session в теле теста как переданный параметр.
Так как scope для фикструры стоит session - драйвер по факту будет создан один раз и просто будет передаваться между тестами. Финализер будет вызван в конце сессии, а не в конце теста.
self = <day_first.test_1.Test_1 object at 0x034607F0>
def test_1(self):
> self.driver.get("http://localhost:8888/about.htm")
E AttributeError: 'module' object has no attribute 'get'
Вот теперь я действительно ничего ен понимаю, что происходит в фикстурах, и почему это объект типа web-driver внезапно стал module типа
Спасибо, такое решение, конечно имеет место быть, но я честно несколько поражен что нет нормального способа передачи.
Второе, насколько я понимаю, все параметры и фикстуры, передаются посредством главного потока, который параметризируется из conftest и тащит к тестам, все то, что было описано. Такая своеобразная инкапсуляция.
Но вопрос как бы не прошел, я в явном виде передаю для тест функции аргумент, не наследуясь и тому подобное, почему так же нельзя сделать для класса?
Классы мне удобны для группировки, и уменьшению копипасты, так как я явно могу наследоваться из них, и не дублировать файлами код.
С другой стороны, мне стало интересно отчего я получаю непонятные результаты из кода (см. пред пост).
В общем пролазил пол дня, ответ так и не нашел на свою проблему(
from common import BaseTest
class TestClass1(BaseTest):
def test_1_1(self, driver):
driver.instance.get('http://lessons2.ru')
def test_1_2(self, driver):
driver.instance.get('http://automated-testing.info')
class TestClass2(BaseTest):
def test_2_1(self, driver):
driver.instance.get('http://lessons2.ru/python-for-testers/')
def test_2_2(self, driver):
driver.instance.get('http://twitter.com/autotestinfo')
test_class_2.py
from common import BaseTest
class TestClass3(BaseTest):
def test_3_1(self, driver):
driver.instance.get('http://www.facebook.com/autotestinfo')
def test_3_2(self, driver):
driver.instance.get('http://vk.com/autotestinfo')
# BaseCase.py
from unittest import TestCase
import pytest
@pytest.mark.usefixtures('d')
class BaseCase(TestCase):
pass
# LoginTest.py
from BaseCase import BaseCase
class TestGoodLogin(BaseCase):
def test_login_good(self):
login_page = LoginPage(self.driver)
profile_page = login_page.login('email@example.com', '123456')
assert profile_page.user_navbar
profile_page.logout()
Все классы наследованные от BaseCase - будут иметь инстанс драйвера (self.driver)
Пример рабочий. Юзается в контексте PageObject/PageElement, но без удобного автокомплита со стороны PyCharm =)
Зачем городить тройное оборачивание с синглтоном - мне не понятно…
Почему не ведомый?
Так же как и вы поднимаю сессионную фикстуру, и отдаю ее моему приложению, в приложении сложены все пейдж-обжекты страниц. Удобно и красиво
class TestClass3(BaseTest):
def setup_class(cls):
#подскажите как сюда передать driver ?
def test_3_1(self, driver):
driver.instance.get('http://www.facebook.com/autotestinfo')
def test_3_2(self, driver):
driver.instance.get('http://vk.com/autotestinfo')
Лучше опишу свою хотелку. Перевожу тесты с codeception + php на связку python + py.test.
Тесты разбиты по классам, по функционалу + логическая связь есть между тестами.
Так вот, перед прогоном нужно входить в систему под определенным пользователем, после теста выходить из системы. Думал это сделать через setup_class и teardown_class но как я понял это для unit стиля http://pytest.org/2.2.4/xunit_setup.html
Как это правильней и лучше реализовать?
Контекст и его подчистка во многих современных тестовых фреймворках\модулях доступна на уровне методов\классов\неймспейсов.
Если вам логин под определенным пользователем нужен на все время работы всех тест-сьютов, смысла делать логин-логаут каждый раз нет - вы только внесете тем самым лишнюю вероятность ловить ошибки логинсистемы поэтому делайте фикстуру на неймспейс.