Dans l'
article sur le test des méthodes publiques, j'ai abordé les tests unitaires de la logique de classe privée. Je pense qu'il vaudrait la peine de refaire la thèse, puisque la majorité, à mon avis, a perçu que nous parlions de tester des méthodes privées, bien qu'il s'agisse de logique privée. Dans cet article, je veux illustrer la thèse principale avec un exemple pratique. Sous kat un exemple avec une petite analyse.
Exemple de stock
Afin d'illustrer le problème, j'ai proposé un exemple. Son idée est tirée d'un vrai projet. Idéalement, comme quelqu'un pourrait le remarquer, la classe devrait être écrite différemment. Mais maintenant, plus personne ne va le réécrire (ou refactoriser), cela coûtera beaucoup de temps pour éditer et tester ce qui fonctionne. Par conséquent, il ne sera pas approuvé. Dans ce cas, vous devez modifier le code et les modifications doivent être correctes. Et donc, voici le code. Une logique importante est concentrée dans la méthode ProcessMessage. Objectif: Introduire un nouveau drapeau de logique métier dans le traitement des messages. Ce drapeau a une logique non triviale.
Comment allez-vous mettre en œuvre et tester le drapeau?
using System; using System.Threading.Tasks; using System.Threading; using System.Messaging; namespace PublicMethodNottrivialSample { public class MessageProcessorService { private object _lockObject = new object(); private CancellationTokenSource _cancellationSource; private Action _receiveMessage; private int _listenerThreads = 5; public void Start() { lock (_lockObject) { _cancellationSource = new CancellationTokenSource(); Task.Factory.StartNew(() => InitializeReceiveLoop(_cancellationSource.Token), _cancellationSource.Token); } } private void InitializeReceiveLoop(CancellationToken cancellationToken) { _receiveMessage = () => { while (!cancellationToken.IsCancellationRequested) { using (MessageQueueTransaction msgTx = new MessageQueueTransaction()) { try { msgTx.Begin();
La classe montre que la seule méthode publique est Start (). Vous pouvez le tester si vous changez la signature, mais dans ce cas, l'interface publique changera. En outre, vous devrez modifier plusieurs méthodes pour renvoyer les threads en cours d'exécution, puis attendre qu'ils se terminent dans le test.
Mais, comme nous le rappelons, l'exigence ne concernait que la mise en œuvre du drapeau dans le processus de traitement d'un message, et n'impliquait pas de changement dans le fonctionnement du mécanisme de réception des messages. Par conséquent, peu importe qui apporte les modifications, je m'attends à ce qu'une seule méthode soit fixée et que le test unitaire ne concerne que la logique mutable. Pour y parvenir, il est difficile de rester dans le cadre du principe: le test s'avérera non trivial, ce qui entraînera soit un refus de l'écrire, soit un code compliqué. C'est ainsi que les tests commencent par la méthode publique. Et puis quelqu'un écrira émotionnellement: «TDD, c'est long», «Le client ne paie pas» ou «Les tests ne fonctionnent pas bien».
En général, il est nécessaire de tester un tel code, mais pas avec des tests unitaires, mais avec des tests d'intégration.
"Vous les dames, ou allez"
Bien sûr, l'écriture d'un test unitaire pour la logique modifiée est nécessaire. Je crois que le dilemme, dans ce cas, est de choisir la méthode la moins chère pour écrire un test, plutôt que du code utile. Ici, je veux dire le fait que quoi que vous fassiez: refactoring pour la méthode publique ou une autre solution - vous le ferez afin d'écrire un test, et non de résoudre l'exigence de la tâche du client. Dans ce cas, il est conseillé d'évaluer le coût et l'effet. En plus de la solution ci-dessus avec refactoring, il existe plusieurs solutions alternatives. J'apporte tout, nous discuterons ci-dessous les avantages et les inconvénients:
- la méthode privée peut être testée
- la méthode peut-elle être rendue publique
- peut être rendu interne
- peut être tiré dans une classe distincte en tant que méthode publique
Si nous comparons les trois premières méthodes avec la solution via la méthode publique, mais toutes nécessitent moins de coûts de main-d'œuvre (avec le privé n'est pas un fait). De plus, tous sont en fait la même solution, avec de légères différences stylistiques. Parce que le résultat sera obtenu de quelque façon que ce soit, donc, à mon avis, le choix en faveur de l'une de ces solutions ne devrait pas être basé sur les capacités techniques du langage, mais sur ce que vous voulez montrer aux autres développeurs:
- si la méthode peut être exécutée depuis n'importe où dans la solution et qu'elle fait partie du comportement de la classe, rendez-la publique et tirez-la dans l'interface de classe;
- si la méthode peut être utilisée à partir de n'importe quelle autre classe, mais uniquement à l'intérieur de cet assembly, faites-la en interne ;
- si la méthode ne peut être utilisée qu'à l'intérieur de la classe principale, où elle peut être appelée à partir de toute autre méthode, alors rendez-la protégée en interne .
Les méthodes internes peuvent être rendues visibles à l'assembly de test à l'aide de l'attribut InternalsVisibleTo et appelées comme d'habitude. Cette approche facilite l'écriture des tests et le résultat sera le même.
Processus de testMessage
Revenons à la tâche. En suivant l'approche ci-dessus, j'apporterais quelques modifications:
- rendrait ProcessMessage public
- créerait une nouvelle méthode interne protégée pour la logique d'indicateur (par exemple GetFlagValue)
- écrirait des tests pour GetFlagValue pour tous les cas
- J'écrirais un test pour ProcessMessage pour m'assurer que GetFlagValue est utilisé correctement: les paramètres sont passés correctement et il est vraiment utilisé
Je précise que je n'écrirais pas de tests unitaires pour tous les cas d'utilisation de GetFlagValue dans la méthode ProcessMessage, à condition que je teste ces cas dans les tests unitaires GetFlagValue. Dans le cas de cas non couverts, ils doivent être ajoutés. Dans ce cas, l'approche principale reste:
- tous les cas sont couverts par les tests unitaires GetFlagValue
- Les tests unitaires ProcessMessage vérifient que la méthode des indicateurs est utilisée correctement
Je crois que conformément à cela, vous ne pouvez écrire qu'un seul test unitaire pour ProcessMessage et plusieurs pour GetFlagValue.
Quelque chose comme ça. Vos opinions