Beim Testen durch die öffentliche Methode beginnt zu stinken (Beispiel)

In dem Artikel über das Testen öffentlicher Methoden habe ich das Testen von Einheiten der Logik privater Klassen angesprochen. Ich denke, es würde sich lohnen, die These zu wiederholen, da die Mehrheit meiner Meinung nach der Ansicht war, dass wir über das Testen privater Methoden sprachen, obwohl es sich um private Logik handelte. In diesem Artikel möchte ich die Hauptthese anhand eines praktischen Beispiels veranschaulichen. Unter kat ein Beispiel mit einer kleinen Analyse.

Aktienbeispiel


Um das Problem zu veranschaulichen, habe ich mir ein Beispiel ausgedacht. Seine Idee stammt aus einem realen Projekt. Wie jemand vielleicht bemerkt, müsste die Klasse im Idealfall anders geschrieben werden. Aber jetzt wird niemand es umschreiben (oder umgestalten), weil Das Bearbeiten und Testen der Funktionsweise kostet viel Zeit. Daher wird es nicht genehmigt. In diesem Fall müssen Sie den Code ändern und die Änderungen müssen korrekt sein. Und so ist hier der Code. Wichtige Logik ist in der ProcessMessage-Methode konzentriert. Ziel: Einführung eines neuen Flags der Geschäftslogik in der Nachrichtenverarbeitung. Dieses Flag hat eine nicht triviale Logik.

Wie werden Sie das Flag implementieren und testen?

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(); //   MessageQueue queue = new MessageQueue(); Message message = queue.Receive(msgTx); //    ,    if (!cancellationToken.IsCancellationRequested) { ProcessMessage(message); } msgTx.Commit(); } catch (Exception ex) { // some logging } } } }; for (int n = 0; n < _listenerThreads; n++) { StartProcessingLoop(cancellationToken); } } private void ProcessMessage(Message message) { //   ,     } private void StartProcessingLoop(CancellationToken cancellationToken) { Task.Factory.StartNew(_receiveMessage, cancellationToken); } } } 

Die Klasse zeigt, dass die einzige öffentliche Methode Start () ist. Sie können es testen, wenn Sie die Signatur ändern. In diesem Fall ändert sich jedoch die öffentliche Schnittstelle. Darüber hinaus müssen Sie mehrere Methoden ändern, um laufende Threads zurückzugeben, und dann warten, bis sie im Test abgeschlossen sind.

Wie wir uns erinnern, betraf die Anforderung jedoch nur die Implementierung des Flags bei der Verarbeitung einer Nachricht und implizierte keine Änderung der Funktionsweise des Mechanismus zum Empfangen von Nachrichten. Unabhängig davon, wer die Änderungen vornimmt, würde ich daher erwarten, dass nur eine Methode festgelegt wird und sich der Komponententest nur auf veränderbare Logik bezieht. Um dies zu erreichen, ist es schwierig, im Rahmen des Prinzips zu bleiben: Der Test wird sich als nicht trivial herausstellen, was entweder eine Verweigerung des Schreibens oder komplizierten Code zur Folge hat. So beginnt das Testen mit der öffentlichen Methode. Und dann wird jemand emotional schreiben: "TDD ist eine lange Zeit", "Kunde zahlt nicht" oder "Tests funktionieren nicht gut".

Im Allgemeinen ist es notwendig, einen solchen Code zu testen, jedoch nicht mit Komponententests, sondern mit Integrationstests.

"Sie Dame oder gehen"


Natürlich muss ein Komponententest für die geänderte Logik geschrieben werden. Ich glaube, dass das Dilemma in diesem Fall darin besteht, den kostengünstigsten Weg zum Schreiben eines Tests zu wählen, anstatt nützlichen Code. Hier meine ich die Tatsache, dass was auch immer Sie tun: Refactoring für die öffentliche Methode oder eine andere Lösung - Sie werden dies tun, um einen Test zu schreiben und nicht die Anforderung aus der Aufgabe des Kunden zu lösen. In diesem Fall ist es ratsam, die Kosten und die Wirkung zu bewerten. Zusätzlich zu der obigen Lösung mit Refactoring gibt es mehrere alternative Lösungen. Ich bringe alles mit, unten werden wir die Vor- und Nachteile diskutieren:

  1. private Methode kann getestet werden
  2. kann die Methode veröffentlicht werden
  3. kann intern gemacht werden
  4. kann als öffentliche Methode in eine separate Klasse gezogen werden

Wenn wir die ersten drei Methoden mit der Lösung durch die öffentliche Methode vergleichen, aber alle von ihnen weniger Arbeitskosten erfordern (mit privat ist keine Tatsache). Außerdem sind alle die gleiche Lösung mit geringfügigen stilistischen Unterschieden. Weil Das Ergebnis wird in keiner Weise erzielt. Daher sollte meiner Meinung nach die Wahl einer dieser Lösungen nicht auf den technischen Fähigkeiten der Sprache beruhen, sondern auf dem, was Sie anderen Entwicklern zeigen möchten:

  1. Wenn die Methode von einer beliebigen Stelle in der Lösung ausgeführt werden kann und Teil des Verhaltens der Klasse ist, machen Sie sie öffentlich und ziehen Sie sie in die Klassenschnittstelle.
  2. Wenn die Methode von einer anderen Klasse aus verwendet werden kann, jedoch nur innerhalb dieser Assembly, führen Sie sie intern aus .
  3. Wenn die Methode nur innerhalb der Hauptklasse verwendet werden kann, wo sie von einer anderen Methode aufgerufen werden kann, machen Sie sie intern geschützt .

Interne Methoden können mit dem Attribut InternalsVisibleTo für die Testassembly sichtbar gemacht und wie gewohnt aufgerufen werden. Dieser Ansatz erleichtert das Schreiben von Tests und das Ergebnis ist das gleiche.

ProcessMessage testen


Kommen wir zurück zur Aufgabe. Nach dem obigen Ansatz würde ich einige Änderungen vornehmen:

  • würde ProcessMessage öffentlich machen
  • würde eine neue geschützte interne Methode für die Flag-Logik erstellen (z. B. GetFlagValue)
  • würde Tests für GetFlagValue für alle Fälle schreiben
  • Ich würde einen Test für ProcessMessage schreiben, um sicherzustellen, dass GetFlagValue korrekt verwendet wird: Die Parameter werden korrekt übergeben und es wird wirklich verwendet

Ich werde klarstellen, dass ich nicht für alle Fälle der Verwendung von GetFlagValue in der ProcessMessage-Methode Komponententests schreiben würde, vorausgesetzt, ich habe diese Fälle in GetFlagValue-Komponententests getestet. Bei ungedeckten Fällen müssen diese hinzugefügt werden. In diesem Fall bleibt der Hauptansatz:

  • Alle Fälle werden durch GetFlagValue-Komponententests abgedeckt
  • ProcessMessage-Komponententests überprüfen, ob die Flag-Methode korrekt verwendet wird

Ich glaube, dass Sie dementsprechend nur einen Komponententest für ProcessMessage und mehrere für GetFlagValue schreiben können.

Irgendwie so. Ihre Meinungen

Source: https://habr.com/ru/post/de420567/


All Articles