Si j'ai une fonction dans mon code qui va comme:
class Employee{
public string calculateTax(string name, int salary)
{
switch (name)
{
case "Chris":
doSomething($salary);
case "David":
doSomethingDifferent($salary);
case "Scott":
doOtherThing($salary);
}
}
Normalement, je refactoriserais ceci pour utiliser le ploymorphisme en utilisant une classe d'usine et un modèle de stratégie:
public string calculateTax(string name)
{
InameHandler nameHandler = NameHandlerFactory::getHandler(name);
nameHandler->calculateTax($salary);
}
Maintenant, si j'utilisais TDD, j'aurais des tests qui fonctionnent sur l'original calculateTax()
avant de refactoriser.
ex:
calculateTax_givenChrisSalaryBelowThreshold_Expect111(){}
calculateTax_givenChrisSalaryAboveThreshold_Expect111(){}
calculateTax_givenDavidSalaryBelowThreshold_Expect222(){}
calculateTax_givenDavidSalaryAboveThreshold_Expect222(){}
calculateTax_givenScottSalaryBelowThreshold_Expect333(){}
calculateTax_givenScottSalaryAboveThreshold_Expect333(){}
Après refactoring, j'aurai une classe Factory NameHandlerFactory
et au moins 3 implémentations de InameHandler
.
Comment dois-je procéder pour refactoriser mes tests? Dois-je supprimer le test unitaire claculateTax()
de EmployeeTests
et créer une classe de test pour chaque implémentation de InameHandler
?
Dois-je également tester la classe Factory?
salary
à la fonction acalculateTax()
été ajoutée. De cette façon, je pense que je vais dupliquer le code de test pour la fonction d'origine et les 3 implémentations de la classe de stratégie.Je commencerai par dire que je ne suis pas un expert du TDD ou des tests unitaires, mais voici comment je testerais cela (j'utiliserai un code pseudo-similaire):
Je testerais donc que la
calculateTax()
méthode de la classe d'employé demande correctement sonNameHandlerFactory
pour unNameHandler
, puis appelle lacalculateTax()
méthode du retournéNameHandler
.la source
Employee.calculateTax()
méthode. De cette façon, vous n'avez pas besoin d'ajouter des tests d'employés supplémentaires lorsque vous introduisez un nouveau NameHandler.Vous prenez une classe (l'employé qui fait tout) et vous créez 3 groupes de classes: l'usine, l'employé (qui ne contient qu'une stratégie) et les stratégies.
Faites donc 3 groupes de tests:
Vous pouvez bien sûr faire des tests automatisés pour l'ensemble du shebang, mais ceux-ci sont maintenant plus comme des tests d'intégration et doivent être traités comme tels.
la source
Avant d'écrire un code, je commencerais par un test pour une usine. Se moquant des choses dont j'avais besoin, je me forcerais à penser aux implémentations et aux cas d'utilisation.
Ensuite, j'implémenterais une usine et continuerais avec un test pour chaque implémentation et enfin les implémentations elles-mêmes pour ces tests.
Enfin, je supprimerais les anciens tests.
la source
À mon avis, vous ne devez rien faire, ce qui signifie que vous ne devez pas ajouter de nouveaux tests.
J'insiste sur le fait qu'il s'agit d'une opinion, et cela dépend en fait de la façon dont vous percevez les attentes de l'objet. Pensez-vous que l'utilisateur de la classe souhaite fournir une stratégie de calcul de la taxe? S'il ne s'en soucie pas, les tests devraient refléter cela, et le comportement reflété par les tests unitaires devrait être qu'ils ne devraient pas se soucier du fait que la classe a commencé à utiliser un objet de stratégie pour calculer la taxe.
J'ai rencontré ce problème plusieurs fois lors de l'utilisation de TDD. Je pense que la raison principale est qu'un objet de stratégie n'est pas une dépendance naturelle, par opposition à une dépendance de frontière architecturale comme une ressource externe (un fichier, une base de données, un service distant, etc.). Puisqu'il ne s'agit pas d'une dépendance naturelle, je ne fonde généralement pas le comportement de ma classe sur cette stratégie. Mon instinct est de ne changer mes tests que si les attentes de ma classe ont changé.
Il y a un excellent article d'oncle Bob, qui parle exactement de ce problème lors de l'utilisation de TDD.
Je pense que la tendance à tester chaque classe distincte est ce qui tue TDD. Toute la beauté de TDD est que vous utilisez des tests pour stimuler les schémas de conception et non l'inverse.
la source