Библиотека pytest

Введение

Примеры

  • 1

    Настройка py.test

    py.test является одним из нескольких библиотек сторонних тестирующих , которые доступны для Python. Он может быть установлен с помощью pip с

     pip install pytest
    
     

    Код для тестирования

    Скажем , мы тестируем функцию добавления в projectroot/module/code.py :

     # projectroot/module/code.py
    def add(a, b):
        return a + b
    
     

    Код тестирования

    Мы создаем тестовый файл в projectroot/tests/test_code.py . Файл должен начинаться с test_ , чтобы быть признанным в качестве файла тестирования.

     # projectroot/tests/test_code.py
    from module import code
    
    
    def test_add():
        assert code.add(1, 2) == 3
    
     

    Запуск теста

    Из projectroot мы просто запустить py.test :

     # ensure we have the modules
    $ touch tests/__init__.py
    $ touch module/__init__.py
    $ py.test
    ================================================== test session starts ===================================================
    platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
    rootdir: /projectroot, inifile:
    collected 1 items
    
    tests/test_code.py .
    
    ================================================ 1 passed in 0.01 seconds ================================================ 
  • 2

    Неудачные тесты

    Неудачный тест предоставит полезный вывод о том, что пошло не так:

     # projectroot/tests/test_code.py
    from module import code
    
    
    def test_add__failing():
        assert code.add(10, 11) == 33
    
     

    Результаты:

    $ py.test
    ================================================== test session starts ===================================================
    platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
    rootdir: /projectroot, inifile:
    collected 1 items
    
    tests/test_code.py F
    
    ======================================================== FAILURES ========================================================
    ___________________________________________________ test_add__failing ____________________________________________________
    
        def test_add__failing():
    >       assert code.add(10, 11) == 33
    E       assert 21 == 33
    E        +  where 21 = <function add at 0x105d4d6e0>(10, 11)
    E        +    where <function add at 0x105d4d6e0> = code.add
    
    tests/test_code.py:5: AssertionError
    ================================================ 1 failed in 0.01 seconds ================================================
  • 4

    Введение в тестовые приспособления

    Более сложные тесты иногда требуют настройки перед запуском кода, который вы хотите протестировать. Это можно сделать в самой тестовой функции, но в результате вы получаете большие тестовые функции, которые делают так много, что трудно сказать, где остановка установки и начало теста. Вы также можете получить много повторяющихся кодов установки между различными функциями тестирования.

    Наш кодовый файл:

     # projectroot/module/stuff.py
    class Stuff(object):
        def prep(self):
            self.foo = 1
            self.bar = 2
    
     

    Наш тестовый файл:

     # projectroot/tests/test_stuff.py
    import pytest
    from module import stuff
    
    
    def test_foo_updates():
        my_stuff = stuff.Stuff()
        my_stuff.prep()
        assert 1 == my_stuff.foo
        my_stuff.foo = 30000
        assert my_stuff.foo == 30000
    
    
    def test_bar_updates():
        my_stuff = stuff.Stuff()
        my_stuff.prep()
        assert 2 == my_stuff.bar
        my_stuff.bar = 42
        assert 42 == my_stuff.bar
    
    
     

    Это довольно простые примеры, но если наш Stuff объекта нужно намного больше настроек, это будет получить громоздким. Мы видим, что между нашими тестами есть некоторый дублированный код, поэтому давайте сначала соберем его в отдельную функцию.

     # projectroot/tests/test_stuff.py
    import pytest
    from module import stuff
    
    
    def get_prepped_stuff():
        my_stuff = stuff.Stuff()
        my_stuff.prep()
        return my_stuff
    
    
    def test_foo_updates():
        my_stuff = get_prepped_stuff()
        assert 1 == my_stuff.foo
        my_stuff.foo = 30000
        assert my_stuff.foo == 30000
    
    
    def test_bar_updates():
        my_stuff = get_prepped_stuff()
        assert 2 == my_stuff.bar
        my_stuff.bar = 42
        assert 42 == my_stuff.bar
    
     

    Это выглядит лучше , но у нас еще есть my_stuff = get_prepped_stuff() вызов захламление наши тестовые функции.

    py.test светильники на помощь!

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

    Сначала мы изменим get_prepped_stuff к арматуре называется prepped_stuff . Вы хотите называть свои приборы именами существительными, а не глаголами, потому что приборы в конечном итоге будут использоваться в самих тестовых функциях. @pytest.fixture указывает , что эта конкретная функция должна быть обработана как приспособление , а не регулярной функции.

     @pytest.fixture
    def prepped_stuff():
        my_stuff = stuff.Stuff()
        my_stuff.prep()
        return my_stuff
    
     

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

     def test_foo_updates(prepped_stuff):
        my_stuff = prepped_stuff
        assert 1 == my_stuff.foo
        my_stuff.foo = 30000
        assert my_stuff.foo == 30000
    
    
    def test_bar_updates(prepped_stuff):
        my_stuff = prepped_stuff
        assert 2 == my_stuff.bar
        my_stuff.bar = 42
        assert 42 == my_stuff.bar
    
     

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

     def test_foo_updates(prepped_stuff):
        assert 1 == prepped_stuff.foo
        prepped_stuff.foo = 30000
        assert prepped_stuff.foo == 30000
    
    
    def test_bar_updates(prepped_stuff):
        assert 2 == prepped_stuff.bar
        prepped_stuff.bar = 42
        assert 42 == prepped_stuff.bar
    
     

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

    Очистка после испытаний.

    Допустим, наш код вырос и наш объект Stuff теперь нуждается в специальной очистке.

     # projectroot/module/stuff.py
    class Stuff(object):
    def prep(self):
        self.foo = 1
        self.bar = 2
    
    def finish(self):
        self.foo = 0
        self.bar = 0
    
     

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

     @pytest.fixture
    def prepped_stuff(request):  # we need to pass in the request to use finalizers
        my_stuff = stuff.Stuff()
        my_stuff.prep()
        def fin():  # finalizer function
            # do all the cleanup here
            my_stuff.finish()
        request.addfinalizer(fin)  # register fin() as a finalizer
        # you can do more setup here if you really want to
        return my_stuff
    
     

    Использование функции финализатора внутри функции может быть довольно сложно понять на первый взгляд, особенно если у вас есть более сложные приборы. Вместо этого вы можете использовать выход приспособление , чтобы сделать то же самое с более читаемом потоком выполнения. Единственное отличие в том , что вместо того , чтобы использовать return мы используем yield на части прибора , где установка делаются и контроль должен идти к тестовой функции, затем добавьте весь код очистки после yield . Мы также украсить его как yield_fixture так , что py.test знает , как справиться с этим.

     @pytest.yield_fixture
    def prepped_stuff():  # it doesn't need request now!
        # do setup
        my_stuff = stuff.Stuff()
        my_stuff.prep()
        # setup is done, pass control to the test functions
        yield my_stuff
        # do cleanup 
        my_stuff.finish()
    
     

    И это завершает вступление к тестовым приспособлениям!

    Для получения дополнительной информации см официальной py.test арматуры документация и документация арматуре официального выхода

Синтаксис

Параметры

Примечания