PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Grundsätzliche Fragen zu Unit-Tests in Golang


Geldmann3
2021-09-16, 17:04:57
Hallo zusammen,

gerade habe ich eine supereinfache Test-Applikation in Golang geschrieben, im Prinzip habe ich 2 Packages, das Main Package und ein ,,server.go" Package, welches lediglich eine StartServer() Funktion hat, die zurzeit nichts weiter tut, als ,,Starting server at port 8080\n" auf der Konsole auszugeben.

package server

import "fmt"

func StartServer() {
fmt.Printf("Starting server at port 8080\n")
}


Nun Frage ich mal ganz pedantisch, angenommen, das sollte von der Funktionsweise so bleiben, schreibt man für sowas einen Unit-Test und wenn ja, wie? Ich müsste ja im Prinzip testen, ob
fmt.Printf("Starting server at port 8080\n")
ausgeführt wurde, bzw, ob die entsprechende Nachricht auf der Konsole ausgegeben wurde.

Kann ich in einer ,,TestStartServer" Funktion irgendwie testen, ob die Ausgabe stattgefunden hat, was ist hier best practice? Oder sollte ich sogar sowas wie ein Printer-Interface bauen, dieses Mocken und dann Expectations auf diesen Mock schreiben? Z.b. mit Gomock. Das erscheint mir für so etwas einfaches allerdings, wie mit einer Kanone auf Spatzen zu schießen. :D

Im Prinzip will ich sicherstellen, dass die Funktion ,,StartServer" immer "Starting server at port 8080\n" printed, egal, was ich in Zukunft an der Funktion verhunze.

universaL
2021-09-16, 17:18:35
mhm,

mocking ist sicher eine möglichkeit, kenne mich in Go nicht aus, dementsprechend keine ahnung ob du fmt "mocken" kannst :-)

kannst du stdout "umleiten" ? dann könntest du nach dem funktionsaufruf gucken, was in "stdout" gelandet ist...

unabhängig davon: fixe port sind mäh :-)

Monger
2021-09-16, 17:54:11
Ich kann beruflich sehr viel zu Testtheorie erzählen, kannst mich gerne auch per PN dazu ansprechen.
Aber erstmal ganz simpel: du solltest mit Unit Tests nur deinen eigenen Code testen. Keine Frameworks, keine Peripherie, und auch nicht deine Kompositionswurzel. Um möglichst viel testen zu können, sollte man "außen" von "innen" möglichst gut trennen.

Geldmann3
2021-09-16, 18:52:30
Danke für das Angebot :smile:, so gesehen müsste ich die Print-Funktion dann doch in ein Interface packen und mocken, wenn ich das richtig verstehe, ich will schließlich testen, ob sie aufgerufen wird, sie aber nicht wirklich mit testen.

Mit Kompositionswurzel meinst du die Main-Funktion?
Das wäre dann doch eher ein Integrationstest, oder?

Monger
2021-09-16, 19:09:22
Ja, das wäre ein Integrationstest.

Gehen wir mal Full Blown: du hast mindestens zwei Peripherie Komponenten: die Konsole, und das Server Framework. Dann hast du ne BL die mit beiden interagiert, und ne Main die alles zusammen bindet. Sowohl für Konsole als auch Server macht ein Adapter inkl. Interface Sinn, weil beides halt sehr statische und schwergewichtige Abhängigkeiten sind. Die Adapter kannst du mit Integrationstests absichern, obwohl man eigentlich davon ausgeht dass die Frameworkentwickler ihren Scheiß selber getestet haben.

Ist totaler Overkill, aber erfahrungsgemäß wächst die BL viel, VIEL schneller als die Peripherie, und dann ist es gut wenn die getestet ist.

Marscel
2021-09-18, 18:41:06
Nun Frage ich mal ganz pedantisch, angenommen, das sollte von der Funktionsweise so bleiben, schreibt man für sowas einen Unit-Test und wenn ja, wie? Ich müsste ja im Prinzip testen, ob
fmt.Printf("Starting server at port 8080\n")
ausgeführt wurde, bzw, ob die entsprechende Nachricht auf der Konsole ausgegeben wurde.

Das sind zwei verschiedene Sachen, die du hier wollen könntest.

1. Unit-Sicht: Nutzt deine Codeeinheit das vorgesehene Interface, und das korrekt?

Du müsstest dafür sorgen, dass du fmt in den Fingern hast. Wenn du über fmt verfügen könntest, kannst du im Test (i. U. zum produktiven fmt) dein eigenes Printf implementieren, und intern die Anzahl von Aufrufen und Parameter merken, und hinterher checken.

Je nach Sprache und Runtime ist das dieser oder jener Akt. Mit Go kenn ich mich zu wenig aus, um zu sagen, wie man eine Standard-Package-Funktion sinnvoll abgreift, als Möglichkeit kann man sich aber, auch aus guter Design-Practice, eigentlich immer vorbehalten, das in irgendeiner Form austauschbar zu injezieren.

(Integerationsebene gibt es hier mEn nicht.)

2. System-Sicht: Landet der Kram in der Ausgabe?

Ob es jetzt auf der Konsole erscheint, also konkreter, ob es in der Standardausgabe des Prozesses erscheint, kannst du dann durch einen externen Runner testen. Der ruft dein Programm auf, und guck die Ausgabe an. Findet der sie nicht: Erwartung nicht erfüllt.

Wie für alles gilt: Schau dir erstmal die etablierten Testing/Mocking-Lösungen an, die decken meist alles ab.