Quality Unit Testing: Teil 2
Wählen Sie Ihre Schaltfläche aus der London School oder Classical School of Unit Testing.
Dies ist Teil II einer Serie zum Testen von Qualitätseinheiten. Im ersten Artikel haben wir uns mit Einheitentests auf hoher Ebene befasst. Wenn Sie den Artikel noch nicht gelesen haben, lesen Sie ihn bitte zuersthttps://hellosagar.hashnode.dev/quality-unit-testing-part-1.
Um ein bisschen von Teil 1 zu rekapitulieren
Unit testing means
1. Testing a piece of code.
2. Blazingly fast to run .relatively to Integration and E2E tests.
3. In an isolated manner.
You can't argue on the first two points, but the third one is where the debate is started.
Here, the isolation can vary depending on the approach you are following. There are mainly two schools of thought are:
- Classical school of unit testing or Detroit or Chicago Style.
- London school of unit testing or Mockist.
Ausstellung (Plot-Kontext)
Wir müssen einen Unit-Test für die Klasse schreiben, Classroomdie das Klassenverhalten einer Schule enthält. Dadurch wird der Benutzer mit einer gültigen Schul-E-Mail-Adresse zur Datenbank hinzugefügt.
class Classroom(
private val admin: Admin
) {
fun add(email: String, type: Type): Boolean {
return if (email.endsWith("@school.com")){
admin.add(email = email, type = type)
}else {
false
}
}
}
class Admin {
private val people: MutableMap<String, Type> = mutableMapOf()
fun get(email: String): Boolean {
return people[email] != null
}
fun add(email: String, type: Type): Boolean {
return if (people[email] == null) {
people[email] = type
true
} else {
false
}
}
}
- SUT oder zu testendes System: Bezieht sich auf die Klasse, die getestet wird.
- MUT oder Method under test: Bezieht sich auf die Methode im SUT, die getestet wird. SUT und MUT werden oft synonym verwendet.
- Abhängigkeit: Objekt, das das SUT benötigt, um korrekt zu funktionieren.
- Doppeltes Testen: Einfachere Version einer Abhängigkeit, um ein einfaches Testen zu ermöglichen.
Es gibt zwei Arten von Abhängigkeiten:
- Gemeinsame Abhängigkeit : Abhängigkeiten, die von verschiedenen Tests gemeinsam genutzt werden, und Änderungen in ihrem Zustand sind für alle Tests sichtbar. Dies ist normalerweise eine Variable auf Klassenebene oder eine statische Variable aus einer anderen Klasse. Beispiele sind Datenbanken und Dateisysteme.
- Private Abhängigkeit : Dies wird nicht von verschiedenen Tests geteilt und ist normalerweise eine Variable auf Methodenebene. Es gibt zwei weitere Arten:-
- Veränderliche Abhängigkeit : Der Zustand eines Objekts wird während der Ausführung des Komponententests geändert.
- Wertobjekt : Unveränderliches Objekt, das einen einfachen Wert darstellt, z. B. eine Zahl oder eine Zeichenfolge, und dem keine Identität oder Verhalten zugeordnet ist.
- Unit : Testet jeweils nur eine einzelne Klasse.
- Isolation : Ersetzen Sie alle Abhängigkeiten durch ein Testdouble, damit sich das SUT ausschließlich auf die einzelne Klasse, dh das SUT, konzentriert.
- Abhängigkeit : Verwendet test double für alle Abhängigkeiten außer unveränderlichen Abhängigkeiten.
- Assert: Es testet die Interaktion, um das erwartete Verhalten zu überprüfen.
- Anordnen: Richten Sie die erforderlichen Vorbedingungen und Eingaben ein, die das Erstellen von Objekten und das Einrichten von Mocks beinhalten können.
- Handeln: Rufen Sie den MUT mit den erforderlichen Eingaben auf.
- Assert: Stellen Sie sicher , dass die Tests das erwartete Ergebnis erbracht haben.
- In Zeile 1 des zweiten Testfalls definieren wir einfach die E- Mail , die als Eingabe für den MUT übergeben werden muss.
- In Zeile 2 verwenden wir anstelle einer tatsächlichen Instanz der
AdminKlasse einen Schein, der als private änderbare Abhängigkeit derClassroomKlasse fungiert. - In Zeile 3 erwähnen wir, dass wir, wenn
add()die MethodeAdmineiner Klasse mit der angegebenen E-Mail und dem angegebenenSTUDENTTyp aufgerufen wird, zurückgeben möchtentrue. - In Zeile 4 richten wir das SUT mit der notwendigen Eingabe, dh der
AdminKlasse, ein. - In Zeile 1 bestätigen wir die vom MUT zurückgegebene Ausgabe und verifizieren auch diese Interaktion. Wenn die E-Mail mit der E- Mail endet
@school, wissen wir, dass die Methode dieserAdminKlasseadd()aufgerufen werden muss, und das haben wir auch verifiziert. - Es eliminiert das Rätselraten darüber, welcher Teil der Codebasis beschädigt ist, was dazu führt, dass der Test fehlschlägt, da es sich ausschließlich auf das SUT-Verhalten konzentriert, indem alle externen Einflüsse entfernt werden.
- Kein Deal mit den kreisförmigen Abhängigkeiten und großen Objektgraphen wie dem russischen Nesting Doll Set :)
- Der größte Nachteil ist die Überspezifikation, die die Tests an die Implementierungsdetails koppelt. Dies geschieht, weil Mocks mächtig sind und mit großer Kraft große Verantwortung einhergeht.
- Einheit : Betrachten Sie eine Einheit als eine Verhaltenseinheit, die zum Testen benötigt wird, als ob sie mehr als eine Klasse benötigt, aber es ist immer eine gute Idee, eine Klasse pro Test beizubehalten.
- Isolation : Dies bedeutet, dass Tests entweder parallel, nacheinander oder in beliebiger Reihenfolge ausgeführt werden, dies sollte sich jedoch nicht auf die Ausgabe der anderen auswirken.
- Abhängigkeit : Verwendet Test Double nur für die gemeinsam genutzten Abhängigkeiten wie Datenbank oder Dateisystem, Rest ist produktionsbereite Objektinstanzen.
- Assert: Es überprüft den Rückgabewert oder die Änderung des Zustands der Klasse.
- In Zeile 1 definieren wir nur die E- Mail , die als Eingabe für den MUT übergeben werden muss.
- In Zeile 2 verwenden wir hier die Objektinstanz anstelle des verspotteten Objekts, wie wir es im Londoner Ansatz getan haben.
- In Zeile 3 richten wir die Vorbedingung so ein, dass sie das Unit-Test-Verhalten rechtfertigt, zum Beispiel prüfen wir hier, dass MUT zurückgeben sollte, wenn die E-Mail bereits existiert,
falsealso fügen wir vor dem Aufrufen des MUT den Wert in dieAdminzu simulierende Klasse ein diese Umgebung. - In Zeile 4 richten wir das SUT mit der notwendigen Eingabe, dh der
AdminKlasse, ein. - Wir überprüfen den Rückgabewert des MUT, aber auch die Änderung des Zustands der
AdminKlasse - Produzierte Tests, die sich auf die Ausgabe oder das Verhalten des Zustands konzentrieren, wodurch es von der Implementierung entkoppelt wird.
- Fördert eine hohe Kohäsion (lose Kopplung), da sie nicht von der Wechselwirkung abhängt.
- Redundante Abdeckung ist ein Anti-Pattern, das mit diesem Ansatz verbunden ist, bei dem mehrere Tests dieselbe Funktionalität adressieren und wenn dieser Code bricht, alle Tests fehlschlagen, was als Ressourcenverschwendung angesehen wird.
Dieser Ansatz ist auch als „ White-Box-Testing“ oder „ Outside-In “-Ansatz bekannt, da der Entwickler in TDD, wenn die Implementierung noch nicht definiert ist, mit der Definition des Verhaltens für Tests beginnen kann, indem er die Abhängigkeit durch Mocks ersetzt, dh Double testet und dann einen Drilldown durchführt von oben nach unten durch die Definition von Komponententests, ohne sich um die Besonderheiten der Klassenimplementierung zu kümmern.
Eigenschaften:
Meine bevorzugte Sprache ist Kotlin und ich verwende mockk , um Abhängigkeiten zu simulieren.
internal class ClassroomLondonStyleTest{
@Test
fun `WHEN email domain name is invalid, THEN returns false`(){
// Arrange
val admin = mockkClass(Admin::class)
val classroom = Classroom(admin)
// Act
val output = classroom.add("[email protected]", Type.STAFF)
// Assert
Assert.assertFalse(output)
verify(exactly = 0) { admin.add(any(), any()) }
}
@Test
fun `WHEN email already exist, THEN returns false`(){
// Arrange
val email = "[email protected]"
val admin = mockkClass(Admin::class)
every { admin.add(email, Type.STUDENT) } returns false
val classroom = Classroom(admin)
// Act
val output = classroom.add(email, Type.STUDENT)
// Assert
Assert.assertFalse(output)
verify(exactly = 1) { admin.add("[email protected]", Type.STUDENT) }
}
@Test
fun `WHEN email is entered first time, THEN returns true`(){
// Arrange
val email = "[email protected]"
val admin = mockkClass(Admin::class)
every { admin.add(email, Type.STUDENT) } returns true
val classroom = Classroom(admin)
// Act
val output = classroom.add(email, Type.STUDENT)
// Assert
Assert.assertTrue(output)
verify(exactly = 1) { admin.add("[email protected]", Type.STUDENT) }
}
}
Wenn Sie bemerkt haben, dass ich den Test in 3 Phasen unterteilt habe, wobei AAA zum Organisieren der Tests verwendet wurde, wurde dies in Martin Fowlers Buch Refactoring: Improving the Design of Existing Code eingeführt .
Beginnend mit der Arrangierphase:
Aufrufen des MUT-dh - add()Verfahrens durch Weiterleiten von Eingaben email, typedie sowohl Wertobjekte sind als auch das Speichern des MUT-Ausgangs für die Bestätigungsphase.
Dann Assert-Phase:
Klassische Unit-Testing-Schule oder Ansatz im Detroit- oder Chicago-Stil
Dieser Ansatz ist auch als „ Black-Box-Testing“ oder „ Inside-Out “-Ansatz bekannt, da wir hier, wenn wir TDD folgen, normalerweise eine tatsächliche Instanz von Objekten verwenden und nichts imitieren, es sei denn, es handelt sich um eine gemeinsame Abhängigkeit . Es beginnt also mit einer Codeeinheit auf niedriger Ebene und arbeitet sich auf dem Weg nach oben vor.
Eigenschaften:
internal class ClassroomChicagoStyleTest{
@Test
fun `WHEN email domain name is invalid, THEN returns false`(){
// Arrange
val admin = Admin()
val classroom = Classroom(admin)
// Act
val output = classroom.add("[email protected]", Type.STAFF)
// Assert
Assert.assertFalse(output)
}
@Test
fun `WHEN email already exist, THEN returns false`(){
// Arrange
val email = "[email protected]"
val admin = Admin()
admin.add(email, Type.STUDENT)
val classroom = Classroom(admin)
// Act
val output = classroom.add(email, Type.STUDENT)
// Assert
Assert.assertFalse(output)
}
@Test
fun `WHEN email is entered first time, THEN returns true`(){
// Arrange
val admin = Admin()
val classroom = Classroom(admin)
// Act
val output = classroom.add("[email protected]", Type.STUDENT)
// Assert
Assert.assertTrue(output)
Assert.assertTrue(admin.get("[email protected]"))
}
}
Nehmen wir auch hier den zweiten Testfall ( WHEN email is entered first time, THEN returns true) zur Erklärung.
Beginnend mit der Arrangierphase:
Aufrufen des MUT, dh add()der Methode, durch Weiterleiten von Eingaben, emailund typebeide sind Wertobjekte und speichern die Ausgabe des MUT der Bestätigungsphase.
Dann Assert-Phase:
Es ist also nicht wie eins über dem anderen, aber es ist ein bisschen von beidem, man muss die Vor- und Nachteile von beiden abwägen und dann eine Entscheidung treffen.
Wenn Sie beispielsweise den Ansatz der London School in Betracht ziehen, geben Sie an, das Risiko der Fehlalarme zu verstehen, die durch zukünftiges Refactoring erzeugt werden können, aber dieses Risiko wirkt sich nur auf eine kleine Untergruppe von Tests aus, wenn Ihre Codebasis modular ist und Sie nicht viel tun von Dingen in einer Klasse selbst. Und wenn Sie den Ansatz der klassischen Schule in Betracht ziehen, müssen Sie das Risiko einer redundanten Berichterstattung verstehen, aber wenn Sie Ihren Test auf jeder PR durchführen, würde es nicht so lange dauern, den Fehler zu erkennen. Ein weiterer Punkt, den Sie berücksichtigen können, ist, dass Sie den klassischen Ansatz verwenden können, wenn Ihr internes Design ein Produkt des Refactorings ist, weil Sie diese Klasse oft überarbeitet haben, oder wenn Ihr internes Design ein Produkt des Upfront-Designs ist, dann können Sie London in Betracht ziehen schulischer Ansatz.
Was Sie also gut verwenden sollten, hängt von Ihrer Situation ab (wie jeder Ingenieur sagt ). Jetzt, da Sie es wissen, können Sie Ihre eigene Wahl treffen.
Danke an Mihir, Nishant, Yogesh und prabhatexit0 für die Überprüfung meines Entwurfs, um Feedback für Korrekturen und Vorschläge zu geben.
Bitte teilen Sie Ihr Feedback und Ihre Vorschläge in den Kommentaren mit und korrigieren Sie mich, wenn Sie mir in einem Punkt nicht zustimmen. Zeigen Sie Ihre Unterstützung, indem Sie etwas geben. Das wird mein Selbstvertrauen stärken, weiterzumachen und es mit anderen Ingenieuren zu teilen. Stellen Sie sicher, dass Sie sich anmelden, um über den nächsten Teil benachrichtigt zu werden, und folgen Sie ihm, um mehr zu erfahren.
Immer bereit , über etwas Cooles zu diskutieren , fühlen Sie sich frei , sich zu verbinden .
LinkedIn : hellosagar
Twitter: hellosagarCode
Github: hellosagar

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































