J'aime vraiment les suggestions de @Hassek et tiens à souligner ce que un excellent moment il fait sur le manque évident de pratiques standard, ce qui est vrai pour beaucoup de Django aspects, et pas seulement les tests, parce que tous nous approche du cadre avec différentes préoccupations à l'esprit, en ajoutant également pour que le plus grand degré de souplesse que nous avons avec la conception de nos applications, nous avons souvent radicalement différentes solutions qui sont applicables pour le même problème.
Ayant dit que, bien que la plupart d'entre nous encore lutter pour un grand nombre des mêmes objectifs lors de l'essai de nos applications, principalement:
- Le maintien de nos modules de test bien organisé
- La création de réutilisables affirmation et méthodes d'assistance, des fonctions d'assistance qui permettent de réduire la LDC pour les méthodes d'essai, afin de les rendre plus compact et plus lisible
- Montrant qu'il est de toute évidence, l'approche systématique à la façon dont les composants de l'application sont testés
Comme @Hassek, ce sont mes préférences qui peuvent, directement en conflit avec les pratiques que vous pouvez appliquer, mais j'ai l'impression que c'est agréable de partager les choses que nous avons prouvé que le travail, si ce n'est dans notre cas.
Pas de cas de test luminaires
Application des luminaires de l'excellent travail, dans le cas où vous avez certaines constantes du modèle de données que vous souhaitez garantir à être présent dans la base de données, dire une collection de villes avec leurs noms et bureau de poste numéros.
Cependant, je vois cela comme une inflexible solution pour fournir des données de scénario de test. Appareils de Test sont très prolixe, modèle mutations de la force vous soit passer par un long processus de reproduire le gabarit de données ou d'effectuer manuel fastidieux des changements et le maintien de l'intégrité référentielle est difficile à effectuer manuellement.
En outre, vous aurez plus de chances d'utiliser de nombreux types d'appareils dans vos tests, et pas seulement pour les modèles: vous souhaitez stocker le corps de la réponse de requêtes à l'API, pour créer des appareils qui cible NoSQL pilotes de base de données, écrire ont des appareils qui sont utilisés pour remplir les données de formulaire, etc.
En fin de compte, en utilisant des Api pour créer des données est concis, lisible et il les rend beaucoup plus facile de repérer les relations, de sorte que la plupart d'entre nous recourir à l'aide de usines pour de la création dynamique d'appareils d'éclairage.
L'utilisation intensive des usines
Usine de fonctions et de méthodes sont préférables à l'éradication de vos données de test. Vous pouvez créer helper usine au niveau du module de fonctions ou de cas de test de méthodes que vous pouvez réutilisez
à travers l'application des tests ou à l'échelle de l'ensemble du projet. En particulier, factory_boy
, que @Hassek mentionne, vous offre la possibilité d'hériter/étendre les données de fixture et ne séquençage automatique, qui peut paraître un peu maladroit, si vous voulez le faire à la main autrement.
Le but ultime de l'utilisation des usines est de couper vers le bas sur le code de la duplication et de rationaliser la façon dont vous créez des données de test. Je ne peux pas vous donner exacte métriques, mais je suis sûr que si vous allez par le biais de vos méthodes de test avec un œil averti, vous remarquerez une grande partie de votre code de test est principalement la préparation des données que vous aurez besoin pour conduire vos tests.
Lorsque cela est fait de manière incorrecte, la lecture et le maintien de tests devient une activité épuisante. Ce qui tend à dégénérer lorsque les données mutations conduisent à ne pas évidentes de l'échec de test à travers le conseil, au point où vous ne serez pas en mesure d'appliquer systématique refactoring efforts.
Mon approche personnelle de ce problème est de commencer avec un myproject.factory
module qui crée d'accès facile, les références à QuerySet.create
méthodes pour mes modèles et aussi pour tout les objets que je pourrais utiliser régulièrement dans la plupart de mes examens:
from django.contrib.auth.models import User, AnonymousUser
from django.test import RequestFactory
from myproject.cars.models import Manufacturer, Car
from myproject.stores.models import Store
create_user = User.objects.create_user
create_manufacturer = Manufacturer.objects.create
create_car = Car.objects.create
create_store = Store.objects.create
_factory = RequestFactory()
def get(path='/', data={}, user=AnonymousUser(), **extra):
request = _factory.get(path, data, **extra)
request.user = user
return request
def post(path='/', data={}, user=AnonymousUser(), **extra):
request = _factory.post(path, data, **extra)
request.user = user
return request
Cela permet à son tour de me faire quelque chose comme ceci:
from myproject import factory as f # Terse alias
# A verbose, albeit readable approach to creating instances
manufacturer = f.create_manufacturer(name='Foomobiles')
car1 = f.create_car(manufacturer=manufacturer, name='Foo')
car2 = f.create_car(manufacturer=manufacturer, name='Bar')
# Reduce the crud for creating some common objects
manufacturer = f.create_manufacturer(name='Foomobiles')
data = {name: 'Foo', manufacturer: manufacturer.id)
request = f.post(data=data)
view = CarCreateView()
response = view.post(request)
La plupart des gens sont rigoureux sur la réduction de la duplication de code, mais j'ai fait intentionnellement introduire certains chaque fois que je sens qu'il contribue à l'essai à l'exhaustivité. Encore une fois, le but, avec quelle que soit l'approche que vous prenez pour des usines est de minimiser la quantité de brainfuck vous introduire dans l'en-tête de chaque méthode de test.
Mocker, mais de les utiliser à bon escient
Je suis un fan de mock
, comme je l'ai développé une appréciation pour l'auteur de la solution à ce que je crois était le problème qu'il voulait aborder. Les outils fournis par le paquet vous permettent de test assertions par l'injection de résultats attendus.
# Creating mocks to simplify tests
factory = RequestFactory()
request = factory.get()
request.user = Mock(is_authenticated=lamda: True) # A mock of an authenticated user
view = DispatchForAuthenticatedOnlyView().as_view()
response = view(request)
# Patching objects to return expected data
@patch.object(CurrencyApi, 'get_currency_list', return_value="{'foo': 1.00, 'bar': 15.00}")
def test_converts_between_two_currencies(self, currency_list_mock):
converter = Converter() # Uses CurrencyApi under the hood
result = converter.convert(from='bar', to='foo', ammount=45)
self.assertEqual(4, result)
Comme vous pouvez le voir, se moque sont vraiment utiles, mais ils ont une fâcheuse effet secondaire: votre se moque de montrer clairement votre faire des hypothèses sur la façon dont il est que votre application se comporte, ce qui introduit de couplage. Si Converter
est refait à utiliser autre chose que l' CurrencyApi
, quelqu'un peut ne pas comprendre pourquoi la méthode de test est soudainement défaut.
Donc, avec un grand pouvoir vient une grande responsabilité--si vous allez être un smartass et mocker pour éviter profondément enracinée test obstacles, vous pouvez complètement dissimuler la véritable nature de votre échec de test.
Par-dessus tout, être cohérent. Très très cohérent
C'est le point le plus important à être fait. Être compatible avec absolument tout:
- comment vous organisez vous dans chacun de vos modules de test
- comment vous présenter des cas de test pour les composants de votre application
- comment vous présenter des méthodes d'essai pour affirmer le comportement de ces composants
- quelle est la structure de méthodes d'essai
- comment l'approche de test de composants communs (la classe de base des points de vue, des modèles, des formes, etc.)
- la manière dont vous appliquez la réutilisation
Pour la plupart des projets, le comment votre collaboration aller à l'approche de test est souvent négligé. Alors que le code de l'application elle-même est parfait--en adhérant à des guides de style, l'utilisation de Python idiomes, la réapplication de Django approche propre à résoudre les problèmes connexes, des manuels d'utilisation des composants de l'infrastructure, etc.--personne n'a vraiment fait un effort pour comprendre comment tester un code valide, utile outil de communication et c'est une honte si, peut-être, avoir des lignes directrices claires pour le code de test est tout ce qu'il faut.