Юнит-тестирование Django для редактирования формы - PullRequest
24 голосов
/ 13 февраля 2010

Возможно, кто-то уже разработал методику снятия скуки для следующего идиоматического юнит-теста:

  1. ПОЛУЧИТЬ URL с уже заполненными данными формы
  2. POST исправленная форма с одним или несколькими отредактированными полями
  3. Проверка ответа (прибыль!)

Шаг 2 - самый утомительный, циклически проходящий через поля формы. Есть ли какие-нибудь способы сэкономить время для тестирования форм Django?

[ Обновление : Я не тестирую обработку форм Django. Я проверяю, что мое приложение выдает правильные ответы, когда пользователь вносит изменения в форму. Это приложение, которое обрабатывает клиническую информацию, следовательно, множество возможных ответов на тест.]

Ответы [ 8 ]

29 голосов
/ 14 февраля 2010

Это зависит от того, что вы пытаетесь проверить. Я бы нацелился на ваши тесты чуть точнее, чем кажется.

Если код, который вы хотите протестировать, является логикой проверки формы, то я бы просто создал экземпляр класса формы непосредственно в ваших тестах, передал ему различные словари данных и вызвал .is_valid (), проверил наличие ошибок или их отсутствие. Нет необходимости привлекать запросы HTML или HTTP.

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

Если вы хотите проверить, что шаблоны содержат правильный HTML-код, чтобы заставить форму работать на самом деле (для того, чтобы отлавливать ошибки, такие как забвение включения формы управления для набора форм в шаблон), я использую WebTest и django-webtest , которые анализируют ваш HTML и позволяют легко заполнять значения полей и отправлять форму, как в браузере.

25 голосов
/ 08 октября 2012

Вы можете использовать response.context и form.initial, чтобы получить значения, которые нужно опубликовать:

update_url = reverse('myobject_update',args=(myobject.pk,))

# GET the form
r = self.client.get(update_url)

# retrieve form data as dict
form = r.context['form']
data = form.initial # form is unbound but contains data

# manipulate some data
data['field_to_be_changed'] = 'updated_value'

# POST to the form
r = self.client.post(update_url, data)

# retrieve again
r = self.client.get(update_url)
self.assertContains(r, 'updated_value') # or
self.assertEqual(r.context['form'].initial['field_to_be_changed'], 'updated_value')
22 голосов
/ 31 августа 2010

django-webtest идеально подходит для таких тестов:

from django_webtest import WebTest

class MyTestCase(WebTest):
    def test_my_view(self)
        form = self.app.get('/my-url/').form
        self.assertEqual(form['my_field_10'].value, 'initial value')
        form['field_25'] = 'foo'
        response = form.submit() # all form fields are submitted

По моему мнению, это лучше, чем twill для тестирования django, потому что он предоставляет доступ к внутренним компонентам django, поэтому поддерживается нативный API django response.context, response.templates, self.assertTemplateUsed и self.assertFormError.

С другой стороны, он лучше, чем собственный тестовый клиент django, потому что у него гораздо более мощный и простой API.

Я немного пристрастен;) но я считаю, что теперь django-webtest является лучшим способом написания тестов django.

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

Непонятно, но можно предположить, что у вас есть такие тесты.

class TestSomething( TestCase ):
    fixtures = [ "..." ]
    def test_field1_should_work( self ):
        response= self.client.get( "url with form data already populated" )
        form_data = func_to_get_field( response )
        form_data['field1']= new value
        response= self.client.post( "url", form_data )
        self.assert()
    def test_field2_should_work( self ):
        response= self.client.get( "url with form data already populated" )
        form_data = func_to_get_field( response )
        form_data['fields']= new value
        response= self.client.post( "url", form_data )
        self.assert()

Во-первых, ты слишком много делаешь. Упростить.

class TestFormDefaults( TestCase ):
    fixtures = [ "some", "known", "database" ]
    def test_get_should_provide_defaults( self ):
        response= self.client.get( "url with form data already populated" )
        self.assert(...)

Вышесказанное доказывает, что значения по умолчанию заполняют формы.

class TestPost( TestCase ):
    fixtures = [ "some", "known", "database" ]
    def test_field1_should_work( self ):
        # No need to GET URL, TestFormDefaults proved that it workd.
        form_data= { expected form content based on fixture and previous test }
        form_data['field1']= new value
        response= self.client.post( "url", form_data )
        self.assert()

Не тратьте время на «получение» каждого «поста». Вы можете доказать - отдельно - что операции GET работают. Когда у вас есть это доказательство, просто сделайте ПОЧТЫ.

Если ваши POSTS сильно зависят от сессии и сохраняют состояние, вы все равно можете выполнить GET, но не беспокойтесь о разборе ответа. Вы можете доказать (по отдельности), что оно имеет правильные поля.

Чтобы оптимизировать свой отдых, учтите это.

class TestPost( TestCase ):
    fixtures = [ "some", "known", "database" ]
    def test_many_changes_should_work( self ):
        changes = [
            ( 'field1', 'someValue', 'some expected response' ),
            ( 'field2', 'someValue' ),
            ...
        ]
        for field, value, expected in changes:
            self.client.get( "url" ) # doesn't matter what it responds, we've already proven that it works.
            form_data= { expected form content based on fixture and previous test }
            form_data[field]= value
            response self.client.post( "url", form_data )
            self.assertEquas( expected, who knows what )

Очевидно, что вышесказанное сработает, но из-за этого количество тестов будет небольшим.

1 голос
/ 13 февраля 2010

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

0 голосов
/ 30 марта 2012

Я бы порекомендовал вам взглянуть на инструменты уровня приемочного тестирования, такие как инфраструктура тестирования робота или letucce, которые считаются именно тем, что вы хотите сделать.form », это больше похоже на приемочное (« черный ящик ») тестирование, чем на юнит-тестирование.

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

0 голосов
/ 13 февраля 2010

Я не понимаю, как и зачем вам нужны юнит-тесты для этого. Похоже, вы тестируете (и корректируете) возможный пользовательский ввод, который очень просто покрывается валидацией форм Джанго (и валидацией модели в 1.2)

0 голосов
/ 13 февраля 2010

Возможно, вы ищете инструменты, которые выполняют тестирование переднего конца, например твил или селен

Оба они генерируют код Python, который может быть включен в тесты django, поэтому при запуске тестов он открывает URL-адреса, публикует данные и проверяет все, что вы захотите!

Это должно помочь вам увидеть эти тесты , написанные для селена, для многоразового приложения django с открытым исходным кодом.

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