Использую Selenium + Allure + Python, подскажите как правильно выглядит запись для сохранения скриншотов если тест упал.
У меня сделанно перехватом JUNIT TestWatchers
@Rule
public TestWatcher watchman = new TestWatcher() {
String fileName;
@Override
protected void failed(Throwable e, Description description) {
screenshot();
}
@Attachment(value = "Page screenshot", type = "image/png")
public byte[] saveScreenshot(byte[] screenShot) {
return screenShot;
}
public void screenshot() {
if (driver == null) {
log.info("Driver for screenshot not found");
return;
}
saveScreenshot(((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES));
}
}
(Лишний код убран)
PS: не заметил что питон, но принцип думаю тот же.
В джаве не очень понимаю, все же подожду примера на пайтоне
Ну например как-то так
with allure.step('Шаг 1'):
try:
browser.get('https://www.google.ru')
except:
allure.attach('error_screen', browser.get_screenshot_as_png(), type=AttachmentType.PNG)
raise
А можно его где-то в методе tearDown написать или же для каждого степа надо прописывать это условие ?
Ну попробую заюзать py.test. Там это точно возможно сделать
Что плохого в том, что ты пропишешь это условие для каждого теста? Как вариант еще наверно можно было бы намутить специальный декоратор
И еще не обязательно прописывать для каждого степа. Оберни все степы в один try:except
Уже писал в нескольких темах, делается как-то так:
@pytest.mark.tryfirst
def pytest_runtest_makereport(item, call, __multicall__):
rep = __multicall__.execute()
setattr(item, "rep_" + rep.when, rep)
return rep
@pytest.fixture(scope="function")
def screenshot_on_failure(request):
def fin():
driver = SeleniumWrapper().driver
attach = driver.get_screenshot_as_png()
if request.node.rep_setup.failed:
allure.attach(request.function.__name__, attach, allure.attach_type.PNG)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
allure.attach(request.function.__name__, attach, allure.attach_type.PNG)
request.addfinalizer(fin)
Все это кладется в conftest.py и тест где идут проверки с web-driver’ом маркируется фикстурой screenshot_on_failure
P.S.: Пользуйтесь поиском по форуму
Я у себя организовал это через хук pytest_exception_interact самого py.test.
def pytest_exception_interact(node, call, report):
driver = node.instance.driver
# ...
allure.attach(
name='Скриншот',
contents=driver.get_screenshot_as_png(),
type=allure.constants.AttachmentType.PNG,
)
# ...
Каждый раз при падении любого теста к нему будет подкреплен соответствующий скриншот.
Все хуки кладутся в conftest.py
.
А что если в тест сьюте где проверяется веб еще идет проверка БД например? Будет ли срабатывание хука на фейле БД теста?
На сколько я знаю, хук pytest_exception_interact срабатывает всегда, когда в тесте возникает какое-либо исключение. Исключение состатвляет те случаи, когда тест обернут в @pytest.mark.xfail(raises=...)
.
Если исключение возникло в коде подготовки стенда, в фикстурах, например, в conftest.py
можно определить другой хук - pytest_internalerror.
Понял, но наверное можно навелосипедить чтобы он как то понимал что за тест.
P.S.: под работой с бд я подразумевал не прекондишн, а например такой тест: заполняете формочку создания юзера, жмякаете ок и смотрите что вылезло сообщение это один тест в сьюте, а второй например чекает что юзер в бд создался.
есть еще способо сделать через EvenFiringDriver
Интересный способ. Спасибо за наводку )
Добрый день, пробую использовать эту фикстуру, получаю ошибку > “TypeError: Object of type ‘bytes’ is not JSON serializable” и вот такое :
self = <allure_pytest.listener.AllureListener object at 0x10f529438>
fixturedef = <FixtureDef name='screenshot_on_failure' scope='function' baseid='simple.py' >
@pytest.hookimpl(hookwrapper=True)
def pytest_fixture_post_finalizer(self, fixturedef):
yield
if hasattr(fixturedef, 'cached_result') and self._cache.get(fixturedef):
container_uuid = self._cache.pop(fixturedef)
> self.allure_logger.stop_group(container_uuid, stop=now())
/usr/local/lib/python3.6/site-packages/allure_pytest/listener.py:140:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.6/site-packages/allure_commons/reporter.py:43: in stop_group
plugin_manager.hook.report_container(container=group)
/usr/local/lib/python3.6/site-packages/allure_commons/logger.py:38: in report_container
self._report_item(container)
/usr/local/lib/python3.6/site-packages/allure_commons/logger.py:30: in _report_item
json.dump(data, json_file, indent=indent, ensure_ascii=False)
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py:179: in dump
for chunk in iterable:
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:430: in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:404: in _iterencode_dict
yield from chunks
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:325: in _iterencode_list
yield from chunks
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:404: in _iterencode_dict
yield from chunks
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:325: in _iterencode_list
yield from chunks
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:404: in _iterencode_dict
yield from chunks
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:437: in _iterencode
o = _default(o)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <json.encoder.JSONEncoder object at 0x10ffdca90>
o = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x04\xb0\x00\x00\x02\xcd\x08\x06\x00\x00\x00?M\x98\xea\x00\x00 \x00IDATx\...1\x90 \x08\x82\x18\xfbTUU\xe2\xbbK\xbe\x83D"\xa1\xdeG\x08\x820\xf9?\rg{\x93\x8b\xbc\x98r\x00\x00\x00\x00IEND\xaeB`\x82'
def default(self, o):
"""Implement this method in a subclass such that it returns
a serializable object for ``o``, or calls the base implementation
(to raise a ``TypeError``).
For example, to support arbitrary iterators, you could
implement default like this::
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
"""
raise TypeError("Object of type '%s' is not JSON serializable" %
> o.__class__.__name__)
E TypeError: Object of type 'bytes' is not JSON serializable
/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py:180: TypeError
Подскажите, почему такое получилось ? Спасибо
Скорее всего у вас питон 3х и у вас строки в байтах. b.decode('utf_8')
вроде так
Подскажите, куда эту строку добавить ?
У меня была такая ошибка при переходе с pytest-allure-adaptor на allure-pytest
метод attach раньше работал по другому , там другой порядок аргументов, сначала name, потом body, потом type
в новом методе сначала body, потом name, потом attachment_type