La principale différence, pour moi, est que les tests d'intégration révèlent si une fonctionnalité fonctionne ou est cassée, car ils stressent le code dans un scénario proche de la réalité. Ils invoquent une ou plusieurs méthodes ou fonctionnalités logicielles et testent s'ils agissent comme prévu.
À l'opposé, un test unitaire testant une seule méthode repose sur l'hypothèse (souvent erronée) que le reste du logiciel fonctionne correctement, car il se moque explicitement de chaque dépendance.
Par conséquent, lorsqu'un test unitaire pour une méthode implémentant une fonctionnalité est vert, cela ne signifie pas que la fonctionnalité fonctionne.
Disons que vous avez une méthode quelque part comme celle-ci:
public SomeResults DoSomething(someInput) {
var someResult = [Do your job with someInput];
Log.TrackTheFactYouDidYourJob();
return someResults;
}
DoSomething
est très important pour votre client: c'est une fonctionnalité, la seule chose qui compte. C'est pourquoi vous écrivez généralement une spécification de concombre en l'affirmant: vous souhaitez vérifier et communiquer que la fonctionnalité fonctionne ou non.
Feature: To be able to do something
In order to do something
As someone
I want the system to do this thing
Scenario: A sample one
Given this situation
When I do something
Then what I get is what I was expecting for
Aucun doute: si le test réussit, vous pouvez affirmer que vous fournissez une fonctionnalité fonctionnelle. C'est ce que vous pouvez appeler la valeur commerciale .
Si vous voulez écrire un test unitaire pour DoSomething
vous, vous devez faire semblant (en utilisant des simulations) que les autres classes et méthodes fonctionnent (c'est-à-dire que toutes les dépendances que la méthode utilise fonctionnent correctement) et affirmer que votre méthode fonctionne.
En pratique, vous faites quelque chose comme:
public SomeResults DoSomething(someInput) {
var someResult = [Do your job with someInput];
FakeAlwaysWorkingLog.TrackTheFactYouDidYourJob(); // Using a mock Log
return someResults;
}
Vous pouvez le faire avec Dependency Injection, ou une méthode d'usine ou n'importe quel Mock Framework ou simplement étendre la classe en cours de test.
Supposons qu'il y ait un bug dans Log.DoSomething()
. Heureusement, la spécification Gherkin le trouvera et vos tests de bout en bout échoueront.
La fonctionnalité ne fonctionnera pas, car elle Log
est interrompue, pas parce qu'elle [Do your job with someInput]
ne fait pas son travail. Et, au fait,[Do your job with someInput]
est la seule responsabilité de cette méthode.
Supposons également Log
est utilisé dans 100 autres fonctionnalités, dans 100 autres méthodes de 100 autres classes.
Oui, 100 fonctionnalités échoueront. Mais, heureusement, 100 tests de bout en bout échouent également et révèlent le problème. Et oui: ils disent la vérité .
Ce sont des informations très utiles: je sais que j'ai un produit cassé. C'est aussi une information très déroutante: elle ne me dit pas où est le problème. Cela me communique le symptôme, pas la cause profonde.
Pourtant, DoSomething
le test unitaire est vert, car il utilise un faux Log
, conçu pour ne jamais se casser. Et, oui: c'est clairement mentir . Il communique qu'une fonctionnalité cassée fonctionne. Comment cela peut-il être utile?
(Si DoSomething()
le test unitaire échoue, assurez-vous: [Do your job with someInput]
a quelques bugs.)
Supposons que ce soit un système avec une classe cassée:
Un seul bogue cassera plusieurs fonctionnalités et plusieurs tests d'intégration échoueront.
D'un autre côté, le même bug cassera un seul test unitaire.
Maintenant, comparez les deux scénarios.
Le même bug cassera un seul test unitaire.
- Toutes vos fonctionnalités utilisant le cassé
Log
sont rouges
- Tous vos tests unitaires sont verts, seul le test unitaire
Log
est rouge
En fait, les tests unitaires pour tous les modules utilisant une fonctionnalité cassée sont verts car, en utilisant des simulations, ils ont supprimé les dépendances. En d'autres termes, ils évoluent dans un monde idéal, complètement fictif. Et c'est le seul moyen d'isoler les bogues et de les rechercher. Les tests unitaires signifient moquerie. Si vous ne vous moquez pas, vous ne faites pas de tests unitaires.
La différence
Les tests d'intégration indiquent ce qui ne fonctionne pas. Mais ils ne servent à rien de deviner où pourrait être le problème.
Les tests unitaires sont les seuls tests qui vous indiquent où se trouve exactement le bogue. Pour dessiner ces informations, ils doivent exécuter la méthode dans un environnement simulé, où toutes les autres dépendances sont censées fonctionner correctement.
C'est pourquoi je pense que votre phrase "Ou est-ce juste un test unitaire qui s'étend sur 2 classes" est en quelque sorte déplacée. Un test unitaire ne doit jamais couvrir 2 classes.
Cette réponse est essentiellement un résumé de ce que j'ai écrit ici: les tests unitaires mentent, c'est pourquoi je les aime .