2010-02-13 4 views
22

Кто-то, вероятно, уже разработали методику для снятия скуки на следующее идиоматическое модульное тестирование:Джанго тестирования для формы редактирования

  1. Получить URL с данными формы уже заселенных
  2. POST переработанного вида с одной или более полей отредактирован
  3. Проверить ответ (прибыль!)

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

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

+0

Вы пишете тесты, которые имеют эти три шага для каждого поля формы? И у ваших форм много полей? У вас много полей для тестирования? Это то, что вы говорите? –

+0

Лот форм, много полей. Но каждый тест - это тест для конкретного результата (например, приложение правильно информирует пользователя, который не может полностью квалифицировать диагноз), а не тест каждого поля. –

ответ

28

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

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

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

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

+0

«Я бы использовал response.context, чтобы вытащить объект формы» <- Выполнение этого сейчас. На самом деле не тестирование HTML, а только результаты процесса. Спасибо за сообщение. –

2

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

+0

Возможно, у него есть пользовательская проверка. –

+2

Да, много пользовательской проверки. Поверь мне, я не собираюсь создавать для себя больше работы. ;-) –

0

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

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

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

0

Я не вижу, как и зачем вам нужны модульные тесты для этого. Звучит так, как будто вы тестируете (и исправляете) возможный ввод пользователя, который очень просто покрывается проверкой формы Django (и проверкой модели в 1.2).

4

Непонятно, но угадайте, что у вас такие тесты.

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 работают. Как только вы получите это доказательство, просто выполните POST.

Если вы используете 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) 

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

+0

Упрощенный, потому что это самый полезный ответ. Сейчас я в основном делаю вашу оптимизацию.GET уже находится в отдельном тесте, но я использую его здесь, чтобы заменить строку: «form_data = {ожидаемое содержимое формы на основе документа и предыдущего теста}», поскольку я выполняю операции с помощью автогенерированных светильников и это экономит работу вручную печатать детали. –

+0

@Jeff Bauer: «это экономит работу вручную, набирая детали»? Какие? Так же рефакторинг данных по умолчанию в 'setUp' или суперкласс или отдельная функция. Существует миллион методов программирования для сокращения избыточности, которые могут быть использованы при модульном тестировании. Почему вы не просто реорганизуете утомительные части? Я не понимаю, что так сложно создать тестовый класс, который как-то оптимален. –

+0

-1 для 'очевидно' – thumbtackthief

20

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 

На мой взгляд, это лучше, чем саржа для тестирования Джанго, поскольку он обеспечивает доступ к Джанго внутренностей так родной Джанго response.context, response.templates, self.assertTemplateUsed и self.assertFormError API поддерживается.

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

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

0

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

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

24

Вы можете использовать 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') 
+0

Не уверен, что это все еще актуально, но я работаю над проектом, застрявшим на Django 1.02, и это r.context [0] ["form"], чтобы получить форму. – Tom

+0

Спасибо, это отлично работает для функционального тестирования просмотров, которые используют Forms/ModelForms! – mrooney

+0

Примечание: имя «форма» не гарантируется и зависит от имени контекста, используемого в вашем представлении. например Если вы это сделаете для проверки администратора Django, это имя «adminform». – Cerin

Смежные вопросы