Как писать современные тесты Python? - PullRequest
12 голосов
/ 28 февраля 2010

Каков последний способ написания тестов Python? Какие модули / фреймворки использовать?

И еще вопрос: doctest тесты все еще имеют какое-либо значение? Или все тесты должны быть написаны в более современной среде тестирования?

Спасибо, Бода Цидо.

Ответы [ 5 ]

12 голосов
/ 28 февраля 2010

Обычным способом является использование встроенного модуля unittest для создания модульных тестов и их объединения в наборы тестов, которые можно запускать независимо. unittest очень похож (и вдохновлен) на jUnit и поэтому очень прост в использовании.

Если вас интересуют самые последние изменения, взгляните на новый доклад Майкла Фурда о PyCon:

PyCon 2010: новые и улучшенные: изменения в unittest

9 голосов
/ 28 февраля 2010

Использование встроенного модуля unittest актуально и просто как никогда. Другие опции модульного тестирования py.test, nose и twisted.trial в основном совместимы с unittest.

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

5 голосов
/ 28 февраля 2010

Я не знаю много о doctests, но в моем университете, тестирование носа преподается и поощряется.

Нос можно установить, следуя этой процедуре (я предполагаю, что вы используете ПК - ОС Windows):

  1. установка setuptools
  2. Запуск командной строки DOS (Пуск -> Все программы -> Стандартные -> Командная строка)
  3. Чтобы этот шаг работал, вы должны быть подключены к Интернету. В DOS введите: C: \ Python25 \ Scripts \ easy_install nose

Если вы работаете в другой ОС, проверьте этот сайт

EDIT

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

Среда DbC, которую я бы порекомендовал для python, называется PyContract Я успешно использовал ее в моей инфраструктуре эволюционного программирования

2 голосов
/ 01 марта 2010

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

Если вы используете setuptools или распространять (вы должны перейти к распространению), вы можете настроить нос в качестве сборщика тестов по умолчанию, чтобы вы могли запускать свои тесты с помощью " python setup.py test "

setup(name='foo',
      ...
      test_suite='nose.collector',
      ...

Теперь запущенный «python setup.py test» вызовет нос, который будет сканировать ваш проект на предмет вещей, похожих на тесты, и запускать их, накапливая результаты. Если у вас также есть doctest в вашем проекте, вы можете запустить тестирование носа с опцией --with-doctest, чтобы включить плагин doctest.

нос также имеет интеграцию с покрытием

nosetests --with-coverage.

Вы также можете использовать опции - cover-html --cover-html-dir , чтобы сгенерировать отчет о покрытии HTML для каждого модуля с выделением каждой строки кода, которая не тестируется. Я не стал бы слишком одержим получением покрытия, чтобы сообщить о 100% тестировании покрытия для всех модулей. Некоторый код лучше оставить для интеграционных тестов, о которых я расскажу в конце.

Я стал большим поклонником minimock, так как это делает тестирование кода с большим количеством внешних зависимостей действительно простым. Хотя он работает действительно хорошо в паре с doctest, его можно использовать с любой средой тестирования, использующей класс unittest.TraceTracker . Я бы посоветовал вам не использовать его для тестирования всего вашего кода, поскольку вы все равно должны попытаться написать свой код, чтобы каждая единица перевода могла тестироваться изолированно, без насмешек. Хотя иногда это невозможно.

Вот (непроверенный) пример такого теста с использованием minimock и unittest:

# tests/test_foo.py
import minimock
import unittest

import foo

class FooTest(unittest2.TestCase):
    def setUp(self):
        # Track all calls into our mock objects. If we don't use a TraceTracker
        # then all output will go to stdout, but we want to capture it.
        self.tracker = minimock.TraceTracker()

    def tearDown(self):
        # Restore all objects in global module state that minimock had
        # replaced.
        minimock.restore()

    def test_bar(self):
        # foo.bar invokes urllib2.urlopen, and then calls read() on the
        # resultin file object, so we'll use minimock to create a mocked
        # urllib2.
        urlopen_result = minimock.Mock('urlobject', tracker=self.tracker)
        urlopen_result.read = minimock.Mock(
            'urlobj.read', tracker=self.tracker, returns='OMG')
        foo.urllib2.urlopen = minimock.Mock(
            'urllib2.urlopen', tracker=self.tracker, returns=urlopen_result)

        # Now when we call foo.bar(URL) and it invokes
        # *urllib2.urlopen(URL).read()*, it will not actually send a request
        # to URL, but will instead give us back the dummy response body 'OMG',
        # which it then returns.
        self.assertEquals(foo.bar('http://example.com/foo'), 'OMG')

        # Now we can get trace info from minimock to verify that our mocked
        # urllib2 was used as intended. self.tracker has traced our calls to
        # *urllib2.urlopen()*
        minimock.assert_same_trace(self.tracker, """\
Called urllib2.urlopen('http://example.com/foo)
Called urlobj.read()
Called urlobj.close()""")

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

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

У меня был большой успех в моем текущем проекте. У меня было около 80% покрытия модульных тестов, а остальной код был таким, как разбор аргументов, диспетчеризация команд и состояние приложения верхнего уровня, что трудно охватить в модульных тестах. Эта программа имеет множество внешних зависимостей, работает около десятка различных веб-сервисов и взаимодействует с около 6000 работающих компьютеров, поэтому запускать ее изолированно оказалось довольно сложно.

Я закончил тем, что написал интеграционный тест, который порождает сервер WSGI, написанный с eventlet и webob , который имитирует все сервисы, с которыми моя программа взаимодействует в работе. Затем обезьяна тестирования интеграции исправляет нашу клиентскую библиотеку веб-службы , чтобы перехватить все HTTP-запросы и отправить их в приложение WSGI. После этого он загружает файл состояния, который содержит сериализованный снимок состояния кластера, и вызывает приложение, вызывая его функцию main (). Теперь все внешние службы, с которыми взаимодействует моя программа, смоделированы, так что я могу запускать свою программу так, как она будет выполняться в производственном режиме повторяемым образом.

0 голосов
/ 01 марта 2010

Важно помнить о doctests, что тесты основаны на сравнении строк, и способ отображения чисел в виде строк будет различаться на разных платформах и даже в разных интерпретаторах Python.

Большая часть моей работы связана с вычислениями, поэтому я использую doctests только для проверки моих примеров и строки версии. Я поместил несколько в __init__.py, поскольку он будет отображаться в качестве первой страницы моей документации по API, сгенерированной эпидок.

Я использую нос для тестирования, хотя мне очень интересно проверить последние изменения в py.test.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...