Архив метки: pytest

pytest продолжение

В первой части статей мы немного познакомились с основными базовыми возможностями pytest. Рассмотрели его особенностей и подходы.

В личных беседах и по почте поступил ряд вопросов и предложений, поэтому продолжению быть. В этой и последующих статьях погрузимся глубже в возможности pytest. Новый материал предлагаю выкладывать в виде поваренной книги — проблема, рецептура, ингредиенты, 5 минут готовки, наслаждение.

Последовательно выполнение тестовых сценариев


В тестировании часто бывают случаи, когда необходимо объединить несколько проверок в один большой тест и выполнить их последовательно. Причем бывает и так, что один сценарий зависит от результатов работы предыдущего (но такое лучше стараться избегать в автотестировании). 

В следующих статьях мы посмотрим как можно избежать этой ситуации пользуясь стандартными средствами пайтеста.

Например, тестируемая программа при первом запуске накатывает схему базы (sqlite чтобы не морочиться) и заполняет её некоторой служебной информацией. Допустим перед нами стоит задача «дешево» протестировать этот модуль. Сходу задачу модно разбить на 2 подзадачи — проверка создания файла базы, затем проверка схемы и в конце проверка данных.

Самый простой вариант — нумерация в названии тестов

# conftest.py
import os
import pytest
import sqlite3


def pytest_configure(config):

    config.DB_PATH = «db.sqlite»


@pytest.fixture(scope=»module»)
def creator(request):
    DB_PATH = request.config.DB_PATH
    con = sqlite3.connect(DB_PATH)
    cur = con.cursor()
    cur.executescript(«»»
        create table person(
            firstname,
            lastname,
            age
        );

        create table book(
            title,
            author,
            published
        );

        insert into book(title, author, published)
        values (
            'Dirk Gently''s Holistic Detective Agency',
            'Douglas Adams',
            1987
        );
        «»»)
    con.commit()
    con.close()
    return DB_PATH


@pytest.fixture
def cursor(request):
    class cursor:

        def __init__(self, dbpath):
            self.dbpath = dbpath
            self.conn = sqlite3.connect(self.dbpath)

        def table_exists(self, table):
            cursor = self.conn.cursor()
            cursor.execute(
                «SELECT name FROM sqlite_master WHERE type = «table»»)
            tables = cursor.fetchall()
            for each in tables:
                if table in each:
                    return True
            return False

        def getrows(self, table):
            cursor = self.conn.cursor()
            cursor.execute(«SELECT * FROM %s;» % (table))
            return cursor.fetchall()

    return cursor(request.config.DB_PATH)

# test.py
def test_1_db_exists(creator):
    assert os.path.exists(creator)


@pytest.mark.parametrize(«table», [«person», «book»])
def test_2_check_scheme(table, creator, cursor):
    result = cursor.table_exists(table)
    assert result


@pytest.mark.parametrize(«table», [«book»])
def test_3_check_rows(table, creator, cursor):
    assert cursor.getrows(table)


Спасибо документации питона за готовые примеры.

У такого подхода есть ряд недостатков.
1. В названии теста появляется нумерация — это лишняя мета информация. (Это может сказаться на тестовом отчете или билд логе CI сервера)
2. Название теста усложняется
3. Вставка нового элемента в середину приведет к смене нумерации всех последующих тестов

Вариант посложнее — маркеры


Свои маркеры мы реализовывать не будем, а используем готовый плагин pytest-ordering. В принципе вся реализация такого плагина составит не больше 100 строчек кода.

# test.py
@pytest.mark.order1
def test_db_exists(creator):
    assert os.path.exists(creator)


@pytest.mark.order2
@pytest.mark.parametrize(«table», [«person», «book»])
def test_check_scheme(table, creator, cursor):
    result = cursor.table_exists(table)
    assert result


@pytest.mark.order3
@pytest.mark.parametrize(«table», [«book»])
def test_check_rows(table, creator, cursor):
    assert cursor.getrows(table)


Наши тестовые функции преобразились, стали более понимаемы для чтения.
Из коробки плагин содержит создание цепочек выполнения, более понятные маркеры (pytest.mark.run('first'), pytest.mark.run('second')) и прочее. Более подробно в микро доку плагина.

Ссылки
[1] https://docs.python.org/2/library/sqlite3.html
[2] http://pytest-ordering.readthedocs.org/en/develop/

Автор: Евгений Курочкин

Часть 4. py.test debug

Мы уже рассмотрели основные принципы написание тестов, но один важный момент упустили, а именно дебаг тестов.

Использование print

Часто люди пользующие питон дебажат свои скрипты с помощью использования принтов. По умолчанияю pytest пишет весь stout и stderr. В предыдущем посте эта тема рассматривалась. Для отключения этой особенности нужно использовать параметр —capture или маску -s.  Читать

Часть 3. Совсем немного о py.test.exe

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

>>> py.test -h

Команды формирования вывода

-q — уменьшает детализацию информации о выполнении теста

-v — увеличивает детализацию о выполнении теста

—capture — параметр для управлением захвата вывода stdout/stderr. Принимает одно из значений fd|sys|no. Более подробно в ссылках
-s — маска для —capture=no. Параметр удобен, если нужно убрать мусор (например, дебажные принты) из информации о выполнившихся тестах.

—tb — отвечает за детализацию вывода трейса. Одно из long/short/line/native/no

Команды запуска 

-k — выполнить лишь заданный тест

—maxfail= — задает максимальное число фейлов, тест удобен при дебаге или если заранее известно что результат тестов будет один и тотже
-x — маска дял —maxfaile=1

—pdb — стартует питоновский дебагер для отладки ошибок. Удобен для отладки тестов (подробнее в одном из следующих постов о дебаге тестов)

Комманды для настройки

—color (yes|no|auto) — цветной вывод в консоли или нет

Совсем небольшой пример

# use_tool.py
import py.test

@py.test.mark.parametrize('num', [i for i in range(5)])
def test_func(num):
    print('ntest_func()')
    assert num % 2

def test_fail():
    print('bla, bla, fail')
    assert False

Выполняем в консоли
>>>py.test use_tool.py -k «test_func» -v -s -x —tb=line

============================= test session starts =============================
platform win32 — Python 2.7.5 — py-1.4.20 — pytest-2.5.2 — C:Python27python.exe
plugins: teamcity-messages
collected 6 items

use_tool.py:37: test_func[0]
test_func()
FAILED

================================== FAILURES ===================================
use_tool.py:40: assert (0 % 2)
!!!!!!!!!!!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!
===================== 1 tests deselected by '-ktest_func' =====================
=================== 1 failed, 1 deselected in 0.04 seconds ====================

Ссылки

[1] http://pytest.org/latest/capture.html
[2] http://pytest.org/latest/customize.html?highlight=maxfail
[3] http://pytest.org/latest/usage.html

Автор: Евгений Курочкин

Часть 2. Фикстуры pytest

Всем привет

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

Что такое test fixtures

В тестировании обычно под этим понимают флоу теста, его контекст.
В py.test предусмотрены следующие типы фикстур: встроенные фикстуры, вспомогательные функции, декораторы

Читать

Часть 1. pytest. Первый взгляд

Каждый девелопер, для которого слова tdd, unittest, ci являются не пустыми, слышал про тестовый фраймворк pytest (он же py.test). По каким-то причинам этот тестовый фраймвор слабо освещен на просторах рунета. В ряде статей постараюсь заполнить этот пробел. Думаю, познакомившись с pytest поближе вы забудете про стандартный unittest.



Преимущества pytest

  • он простой
  • он функциональный, в коробке идет большое количество киллер фич
  • он логичный
  • он расширяемый
  • он умный, можно запускать тесты на unittest, doctest
  • код тестов меньше и проще

Ставим


pip install -U pytest
Или через easy_install
easy_install -U pytest

Или из пакета
Качаем пакет http://pypi.python.org/pypi/pytest

python pytest/setup.py install

Единственно при оффлайн установке стоит учесть что для работы pytest нужно еще 2 пакета — colorama и py

Пробуем


Забегая в перед скажу что все тестовые функции должны начинаться с префикса test_*.

# tests.py
def test_first_our_passed():
    assert True

def test_first_our_failed():
    assert False

Запустим тест в консоли py.test test.py

============================= test session starts =============================
platform win32 — Python 2.7.5 — py-1.4.20 — pytest-2.5.2
collected 2 items

tests_first.py .F

================================== FAILURES ===================================
____________________________ test_first_our_failed ____________________________

    def test_first_our_failed():
>       assert False
E       assert False

tests_first.py:6: AssertionError
===================== 1 failed, 1 passed in 0.13 seconds ======================

Из коробки получаем довольно информативный вывод


Структура проекта


Есть два устоявшихся принципа по организации тестового проекта
Все тесты находятся в папке test рядом с пакетами проекта

/myproj
    __init__.py
    project.py
/test
    test_func_1.py
    test_func_2.py

Второй вариант — папка с тестами находится в самом пакете проекта

/myproj
    __init__.py
    project.py
    /test
        test_func_1.py
        test_func_2.py

Разница не большая, а скорее дело вкуса.


Структура тестов


В принципа здесь все банально. Для рассмотрения будем использовать простой тестовый проект.

/myproj
    __init__.py
    project.py

# project.py
def pp(val):
    return val + 1

Первый принцип — все тесты описаны в виде тестовых функциях

def test_positive():
    assert pp(1) == 2

def test_negative():
    assert pp(1) == 1

Второй принцип — все тесты описаны в виде методов тестового класса

class TestProject:
    def test_positive(self):
        assert pp(1) == 2

    def test_negative(self):
        assert pp(1) == 1

На тестах данные принцип практически не сказывается.

Варианты запуска

Из коробки в pytest предусмотрено несколько вариантов запуска тестов.

Через специальную утилиту py.test.exe

py.test.exe test_project.py

В консоли через питон 

python -m «pytest.main('test directory')»

Вызов pytest в коде

if __name__ == '__main__':
    pytest.main('test_project.py')

+ никто не отменял сабпроцесс;)

Ссылки

http://pytest.org

Автор: Евгений Курочкин