Test unitaire Django pour le formulaire edit

Quelqu'un a probablement déjà développé une technique pour soulager l'ennui pour le test unitaire idiomatique suivant:

  1. obtenir une url avec des données de formulaire déjà renseignées
  2. publier un formulaire révisé avec un ou plusieurs champs modifiés
  3. Vérifier la réponse (profit!)

L'Étape 2 est la plus fastidieuse, parcourant les champs de formulaire. Y a-t-il des hacks qui permettent de gagner du temps pour tester les formulaires Django?

[Update : Je ne teste pas la gestion des formulaires Django. Je suis vérifier que mon application produit de réponses correctes lorsqu'un utilisateur apporte des modifications à un formulaire. Il s'agit d'une application qui traite les informations cliniques, d'où beaucoup de réponses possibles à tester.]

24
demandé sur Jeff Bauer 2010-02-13 18:01:24

8 réponses

Cela dépend de ce que vous essayez de tester. Je ciblerais vos tests un peu plus finement qu'il n'y paraît.

Si le code que vous devez tester est la logique de validation de formulaire, alors je voudrais simplement instancier la classe de formulaire directement dans vos tests, lui passer divers dictionnaires de données et appeler .is_valid(), vérifier les erreurs appropriées ou leur absence. Pas besoin d'impliquer des requêtes HTML ou HTTP.

Si c'est la logique de vue (dont IMO devrait être minimisée) que vous testez, vous vous voudrez probablement utiliser le client de test, mais vous ne devriez pas avoir besoin de faire des tests en plusieurs étapes ou de très nombreux tests à ce niveau. Dans la logique de vue de test, je ne gratterais pas le HTML (c'est-à-dire les modèles de test), j'utiliserais la réponse.contexte pour extraire l'objet de formulaire du contexte.

Si ce que vous voulez tester est que les modèles contiennent le code HTML approprié pour que le formulaire fonctionne réellement (afin d'attraper des erreurs comme oublier d'inclure le formulaire de gestion pour un formset dans le modèle), j'utilise WebTest et django-webtest , qui analysent votre code HTML et facilitent le remplissage des valeurs des champs et la soumission du formulaire comme le ferait un navigateur.

28
répondu Carl Meyer 2011-03-28 22:31:40

Vous pouvez utiliser response.context et form.initial pour obtenir les valeurs que vous avez besoin de poster:

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')
25
répondu chrisv 2012-10-08 12:58:42

Django-webtest est parfait pour de tels tests:

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

À mon avis, c'est mieux que twill pour les tests django car il donne accès aux internes de django, donc les django natifsresponse.context, response.templates, self.assertTemplateUsed et self.assertFormError API est pris en charge.

D'autre part, il est meilleur que le client de test django natif car il a une API beaucoup plus puissante et facile.

Je suis un peu biaisé;) mais je crois que django-webtest est maintenant le meilleur moyen d'écrire des tests django.

22
répondu Mikhail Korobov 2012-03-16 12:23:00

Ce n'est pas clair mais une supposition est que vous avez des tests comme celui-ci.

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()

D'abord, tu en fais trop. Simplifier.

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(...)

Ce qui précède prouve que les valeurs par défaut remplissent les formulaires.

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()

Ne perdez pas de temps à faire un "get" pour chaque "post". Vous pouvez prouver -- séparément -- que les opérations GET fonctionnent. Une fois que vous avez cette preuve, faites simplement les messages.

Si vos messages sont très spécifiques à la session et avec état, vous pouvez toujours faire un GET, mais ne vous embêtez pas à analyser le réponse. Vous pouvez prouver (séparément) qu'il a exactement les champs.

Pour optimiser votre repos, considérez ceci.

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 )

Ce qui précède fonctionnera évidemment, mais le nombre de tests semble faible.

5
répondu S.Lott 2010-02-14 12:19:13

Réfléchissez bien à la raison pour laquelle vous devez tester cela à l'unité. Les formulaires font partie de la fonctionnalité de base de Django, et en tant que tels sont très bien couverts par les propres tests unitaires de Django. Si tout ce que vous faites est la création/mise à jour de base, ce qui, d'après votre question, semble être le cas, je ne vois aucune raison d'écrire des tests unitaires pour cela.

2
répondu Daniel Roseman 2010-02-13 17:08:45

Je ne vois pas comment ni pourquoi vous avez besoin de tests unitaires pour cela. Il me semble que vous testez (et corrigez) l'entrée utilisateur possible, qui est couverte très simplement avec la validation de formulaire de Django (et la validation de modèle en 1.2)

0
répondu jonwd7 2010-02-13 17:53:31

Je vous recommande de jeter un oeil dans les outils de niveau de test d'acceptation comme robot test framework ou letucce qui sont pensés juste pour vous voulez faire "je vérifie que mon application produit des réponses correctes lorsqu'un utilisateur apporte des modifications à un formulaire", cela ressemble plus à des tests d'acceptation (boîte noire) qu'à des tests unitaires.

Pour instace, Robot vous permet de définir vos tests sous forme de tableau, vous définissez le flux de travail une fois et vous pouvez ensuite définir facilement un tas de tests qui exercent le flux de travail avec des données différentes.

0
répondu kgu 2012-03-30 12:12:10

Vous cherchez peut-être des outils qui font des tests frontaux comme twill ou selenium

Les deux génèrent du code python, qui peut être inclus dans les tests django, donc lorsque vous exécutez des tests, il ouvre les URL, poste les données et inspecte ce que vous voulez!

Cela devrait vous aider à voir ces tests écrits pour selenium, pour une application django réutilisable open source.

0
répondu Lakshman Prasad 2016-09-01 07:47:22