Как написать unit test для просмотра django?

У меня проблемы с пониманием того, как модульные тесты должны быть разработаны для django.

По моему мнению, тестирование всего взгляда в один ход кажется невозможным. Нам нужно различать предварительные и пост-состояния запроса. Но я понятия не имею, как это сделать. Есть ли реальный пример жизни?

При взгляде на документацию примеры слишком упрощены и ориентированы только на модель.

@login_required
def call_view(request, contact_id):
    profile = request.user.get_profile()
    if request.POST:        
        form = CallsForm(profile.company, request.POST)           
        if form.is_valid()
        return HttpResponseRedirect('/contact/' + contact_id + '/calls/')
    else:        
        form = CallsForm(profile.company, instance=call)              
    variables = RequestContext(request, {'form':form}
    return render_to_response('conversation.html', variables)

обновление:

пытается сделать успешную тестовую работу, но все равно не удается:

def test_contact_view_success(self):
    # same again, but with valid data, then
    self.client.login(username='username1', password='password1')
    response = self.client.post('/contact/add/', {u'last_name': [u'Johnson'], }) 
    self.assertRedirects(response, '/')

сообщение об ошибке:

AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)

Я думаю, это потому, что form.is_valid() терпит неудачу и не перенаправляет, исправить?

Ответ 1

NB NB! То, что я описываю ниже, не является строго "unit test"; почти невозможно записать независимый unit test для кода представления Django. Это скорее интеграционный тест...

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

  • GET или POST анонимным пользователем (необходимо перенаправить на страницу входа)
  • GET или POST зарегистрированным пользователем без профиля (должен поднять исключение UserProfile.DoesNotExist)
  • GET зарегистрированным пользователем (должен показать форму)
  • POST зарегистрированным пользователем с пустыми данными (должны отображаться ошибки формы)
  • POST зарегистрированным пользователем с недопустимыми данными (должны отображаться ошибки формы)
  • POST зарегистрированным пользователем с допустимыми данными (необходимо перенаправить)

Тестирование 1. действительно просто тестирует @login_required, поэтому вы можете пропустить его. Я все равно тестирую его (на всякий случай я или кто-то еще забыл использовать этот декоратор).

Я не уверен, что случай сбоя (500 страниц ошибок) в 2. - это то, что вы действительно хотите. Я бы решил, что вы хотите вместо этого (возможно, использовать get_or_create() или поймать исключение DoesNotExist и создать новый профиль таким образом).

В зависимости от того, сколько пользовательских проверок у вас есть, 4. действительно не нужно тестировать.

В любом случае, учитывая все вышеизложенное, я бы сделал что-то вроде:

from django.test import TestCase

class TestCalls(TestCase):
    def test_call_view_denies_anonymous(self):
        response = self.client.get('/url/to/view', follow=True)
        self.assertRedirects(response, '/login/')
        response = self.client.post('/url/to/view', follow=True)
        self.assertRedirects(response, '/login/')

    def test_call_view_loads(self):
        self.client.login(username='user', password='test')  # defined in fixture or with factory in setUp()
        response = self.client.get('/url/to/view')
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'conversation.html')

    def test_call_view_fails_blank(self):
        self.client.login(username='user', password='test')
        response = self.client.post('/url/to/view', {}) # blank data dictionary
        self.assertFormError(response, 'form', 'some_field', 'This field is required.')
        # etc. ...

    def test_call_view_fails_invalid(self):
        # as above, but with invalid rather than blank data in dictionary

    def test_call_view_fails_invalid(self):
        # same again, but with valid data, then
        self.assertRedirects(response, '/contact/1/calls/')

Очевидно, что недостатком здесь является жестко закодированные URL-адреса. Вы можете использовать reverse() в своих тестах или создавать запросы, используя RequestFactory и вызовите ваши представления как методы (а не по URL-адресу). Однако при последнем методе вам все равно нужно использовать жестко заданные значения или reverse() для проверки целей перенаправления.

Надеюсь, что это поможет.

Ответ 2

Django поставляется с тестовым клиентом, который может быть использован для проверки полного цикла запроса/ответа: Документы содержат пример получения запрос на заданный URL-адрес и утверждение кода состояния, а также контекста шаблона. Вам также понадобится тест, который выполняет POST и утверждает успешную переадресацию, как ожидалось.