Wie kann diese Funktion testbar gemacht werden?

8

Ich habe eine Funktion, die dafür verantwortlich ist, eine Reihe von Konfigurationen zu sammeln und aus all diesen Teilen eine größere Konfiguration zu erstellen. So ist es im Grunde:

%Vor%

Ich habe momentan Komponententests für getThis , getThat , createThatThing , aber nicht für applyUpdate . Ich möchte nicht erneut testen, was getThis und etc. tun, ich möchte nur die für applyUpdate und nur für stub getThis spezifische Logik testen. In einem objektorientierten Stil würden diese über eine Schnittstelle durch Dependency-Injection weitergegeben werden. In einem funktionalen Stil bin ich unsicher, wie es weitergehen soll:

%Vor%

Dies scheint das funktionale Äquivalent von Bastard Injection zu sein, aber darüber hinaus geht es mir hauptsächlich um:

  • jetzt ist mein Code schwerer zu folgen, weil du nicht nur F12 (Go zur Definition) in Funktionen; Dieses Problem besteht auch mit OO-Abhängigkeitsinjektion, wird jedoch durch Tooling gemindert (d. H. Resharper Go Zur Implementierung).
  • Die Funktion, die ich teste, ist technisch nicht die, die vom Produktionscode aufgerufen wird (es könnte Fehler im Mapping geben)
  • Ich sehe für diese "testbare" Version der Funktion
  • keinen guten Namen
  • Ich verschmutze das Modul mit doppelten Definitionen von allem

Wie gehen Sie mit diesen Problemen bei der funktionalen Programmierung um?

    
Asik 29.07.2016, 17:00
quelle

2 Antworten

9

Du hast gesagt:

  

In einem objektorientierten Stil würden diese über eine Schnittstelle durch Dependency-Injection übergeben.

Und derselbe Ansatz wird in FP verwendet, aber anstatt über den Objektkonstruktor zu injizieren, "injizieren" Sie als Parameter die Funktion.

Sie sind also mit Ihrem applyUpdateTestable auf dem richtigen Weg, außer dass dies auch als echter Code verwendet wird, nicht nur als testbarer Code.

Hier ist zum Beispiel die Funktion mit den drei zusätzlichen Abhängigkeiten, die übergeben wurden:

%Vor%

Dann injizieren Sie im Code "production" die echten Abhängigkeiten:

%Vor%

oder einfacher, mit partieller Anwendung:

%Vor%

und in der Testversion injizieren Sie stattdessen die Mocks oder Stubs:

%Vor%

Im obigen Beispiel "production" habe ich die Abhängigkeiten von den Real -Funktionen fest programmiert, aber alternativ Genau wie bei der OO-Stil-Abhängigkeitsinjektion könnte die Produktion applyUpdate von einem Top-Level-Koordinator erstellt werden und ging dann in die Funktionen, die es brauchen.

Dies beantwortet Ihre Fragen, ich hoffe:

  • Derselbe Kerncode wird sowohl für die Produktion als auch für das Testen verwendet
  • Wenn Sie die Abhängigkeiten statisch fest codieren, können Sie weiterhin F12 verwenden, um in die Abhängigkeiten zu dringen.

Es gibt komplexere Versionen dieses Ansatzes, wie die "Reader" -Monade, aber der obige Code ist der einfachste Ansatz, um damit zu beginnen.

Mark Seemann hat eine Reihe guter Posts zu diesem Thema, wie Integration Testing und SOLID: Der nächste Schritt ist Functional und Ports and Adapters .

    
Grundoon 29.07.2016, 22:00
quelle
3

Die Antwort von Scott (@Grundoon) deckt die direktere Übersetzung von OOP nach FP ab. Es ist angemessen, wenn Sie erwarten, dass eine der Funktionen getThis , getThat unrein ist.

Im Allgemeinen ist das Übergeben von Funktionen als Argumente an andere Funktionen ziemlich funktional (die Empfangsfunktion wird dann als eine Funktion höherer Ordnung bezeichnet), sollte aber im Interesse ausgeführt werden Variabilität zu ermöglichen. Das Hinzufügen von zusätzlichen Funktionsargumenten nur für Testzwecke führt zu dem, was David Heinemeier Hansson testinduzierter Schaden nennt >.

In dieser Antwort möchte ich eine andere Perspektive anbieten, obwohl ich betonen möchte, dass Scotts Antwort mit meinem eigenen Denken übereinstimmt (und dass ich es aufgewertet habe). Es passt F #, weil F # eine hybride Sprache ist und implizit unreine Funktionen möglich sind.

In einer streng funktionalen Sprache (wie Haskell) sind Funktionen jedoch standardmäßig rein . Wenn wir annehmen, dass getThis , getThat usw. alle referenziell transparent (rein) sind, können Funktionsaufrufe durch ihre Rückgabewerte ersetzt werden.

Dies bedeutet, dass Sie nicht durch Testdoppel ersetzen müssen.

Stattdessen können Sie Ihre Tests einfach so schreiben:

%Vor%

Man könnte argumentieren, dass dieser Test nur den Produktionscode dupliziert, aber auch einen Test, der Testdoppel im OO-Stil verwendet. Ich gehe davon aus, dass das eigentliche Problem komplexer ist als das OP, was einen Test der Funktion applyUpdate nicht wirklich zu rechtfertigen scheint.

Sie könnten auch argumentieren, dass dieser Test kein Unit -Test ist, und ich stimme der Semantik zu; Ich rufe solche Tests Fassadentests .

Reine Funktionen sind an sich testbar , also gibt es keinen Grund dazu ändere ihr Design, um sie testbar zu machen.

    
Mark Seemann 31.07.2016 07:14
quelle