Wird ein Unit-Test als spröde angesehen, wenn er bei einer Änderung der Geschäftslogik fehlschlägt?

27

Bitte beachten Sie den Code unten; Es wird geprüft, ob eine Person mit einem weiblichen Geschlecht zum Angebot berechtigt ist1:

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id,"Offer1");
    Assert.False(offer1.IsEligible(person));
}

Dieser Komponententest ist erfolgreich. Es wird jedoch scheitern, wenn 'Offer1' künftig Frauen angeboten wird.

Ist es akzeptabel zu sagen - wenn sich die Geschäftslogik um Angebot 1 ändert, muss sich der Komponententest ändern. Bitte beachten Sie, dass in einigen Fällen (für einige Angebote) die Geschäftslogik in der Datenbank wie folgt geändert wird:

update Offers set Gender='M' where offer=1;

und in einigen Fällen im Domain-Modell wie folgt:

if (Gender=Gender.Male)
{
  //do something
}

Bitte beachten Sie auch, dass sich in einigen Fällen die dahinter stehende Domain-Logik regelmäßig ändert und in einigen Fällen nicht.

w0051977
quelle
2
Denken Sie aus einem anderen Blickwinkel: Möchten Sie Tests haben, die nicht fehlgeschlagen sind, wenn Sie die Logik im zu testenden System ändern?
Fabio

Antworten:

77

Dies ist im üblichen Sinne nicht spröde. Ein Komponententest wird als spröde angesehen, wenn er aufgrund von Implementierungsänderungen abbricht, die das zu testende Verhalten nicht beeinflussen. Aber wenn die Business - Logik selbst ändert, dann wird ein Test dieser Logik ist angeblich zu brechen.

Wenn sich die Geschäftslogik jedoch häufig ändert, ist es möglicherweise nicht angebracht, die Erwartungen in die Komponententests einzubeziehen. Stattdessen können Sie testen, ob sich die Konfigurationen in der Datenbank erwartungsgemäß auf die Angebote auswirken.

Der Name des Tests Returns False When Given A Person With A Gender Of Femalebeschreibt keine Geschäftsregel. Eine Geschäftsregel wäre so etwas wie Offers Applicable to M should not be applied to persons of gender F.

So können Sie einen Test, bestätigt schreiben könnten , dass , wenn ein Angebot als nur anwendbar definiert ist M Personen zu geben, dann wird eine Art F Person nicht als für sie angezeigt werden. Dieser Test stellt sicher, dass die Logik auch dann funktioniert, wenn sich die Konfiguration der spezifischen Angebote ändert.

JacquesB
quelle
@JaquesB, wäre es dann kein Unit Test? oder würde es? Ich glaube, es wäre ein Integrationstest, wenn die Datenbank beteiligt wäre. Ist das richtig? Wollen Sie damit sagen, dass Sie keine Komponententests verwenden sollten, wenn sich die Geschäftslogik stark ändert?
w0051977
@ w0051977: Hängt davon ab, wie Sie den Test schreiben. Wenn der Test beinhaltet, dass tatsächlich Änderungen an einer Datenbank vorgenommen werden, handelt es sich um einen Integrationstest.
JacquesB
3
@ w0051977 Bessere Idee - Das Repository muss nicht eine Abhängigkeit der Komponente sein, die für die Implementierung von Geschäftsregeln verantwortlich ist. Verfügen Sie über eine übergeordnete Orchestrierung, die das Repository aufruft und dann die Geschäftsregeln aufruft. Jetzt können Sie die Geschäftsregeln einzeln testen.
Ant P
5
@ w0051977 natürlich ist es - tests spezifizieren verhalten. Wenn sich die Regeln für das Verhalten einer Komponente ändern, müssen sich die Tests ändern, um die Verhaltensänderung widerzuspiegeln. Was nicht geändert werden muss, sind Tests, die andere Verhaltensweisen als die Änderungen angeben. Wenn das Verhalten von der Datenbank bestimmt wird, ist ein Test für einen anderen Code inhärent unabhängig und sollte nicht geändert werden müssen, es sei denn, diese Datenbanklogik liegt im Bereich des Tests. Dieser Bereich ist für Sie selbst zu definieren und die Semantik, ob es sich um einen Komponententest oder einen Integrationstest handelt, ist nicht wirklich wichtig.
Ant P
3
@ w0051977 diese Idee etwas erweitern, wenn sich die Geschäftslogik ändert oder ein Fehler behoben ist und die Tests nicht angepasst werden müssen, ist dies ein Zeichen dafür, dass die Tests nicht genügend Fälle abdecken und im Allgemeinen erweitert werden sollten.
Morgen
14

Wenn die Eigenschaft in der Produktionsdatenbank (oder einem Klon zum Testen) definiert ist, handelt es sich nicht um einen Komponententest . Ein Komponententest überprüft eine Arbeitseinheit und erfordert keinen bestimmten externen Zustand, um zu arbeiten. Dies setzt voraus, dass Offer1in der Datenbank ein Angebot nur für Männer definiert ist. Das ist ein äußerer Zustand. Dies ist also eher ein Integrationstest , insbesondere ein System- oder Abnahmetest . Beachten Sie, dass Akzeptanztests häufig nicht per Skript ausgeführt werden (nicht in einem Testframework ausgeführt, sondern manuell von Menschen durchgeführt).

Wenn die Eigenschaft im Domänenmodell mit einer ifAnweisung definiert ist, ist derselbe Test ein Komponententest. Und es kann spröde sein. Das eigentliche Problem ist jedoch, dass der Code spröde ist. In der Regel ist Ihr Code widerstandsfähiger, wenn das Geschäftsverhalten konfigurierbar und nicht fest codiert ist. Weil eine schnelle Bereitstellung zur Behebung eines kleinen Codierungsfehlers selten sein sollte. Eine Geschäftsanforderung, die sich ohne Vorankündigung ändert, ist jedoch nur ein Dienstag (was wöchentlich geschieht).

Möglicherweise verwenden Sie ein Unit-Test-Framework, um den Test auszuführen. Unit-Test-Frameworks sind jedoch nicht auf die Ausführung von Unit-Tests beschränkt. Sie können und können auch Integrationstests durchführen.

Wenn Sie einen Komponententest schreiben würden, würde erstellen Sie beide personund offer1von Grund auf ohne Vertrauen auf Datenbankstatus. So etwas wie

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id, "ReturnsFalseWhenGivenAPersonWithAGenderOfFemale");
    offer1.markLimitedToGender("M");

    Assert.False(offer1.IsEligible(person));
}

Beachten Sie, dass sich dies basierend auf der Geschäftslogik nicht ändert. Es ist keine Behauptung, offer1die Frauen ablehnt. Es ist offer1die Art von Angebot, das Frauen ablehnt.

Sie können die Datenbank als Teil des Tests erstellen und konfigurieren. In C # mit NUnit oder in Java JUnit würden Sie die Datenbank in einer SetupMethode einrichten . Vermutlich hat Ihr Test-Framework eine ähnliche Vorstellung. Bei dieser Methode können Sie Datensätze mit SQL in die Datenbank einfügen.

Wenn Sie Schwierigkeiten haben, Code zu schreiben, der die Produktionsdatenbank durch eine Testdatenbank ersetzt, klingt dies nach einer Testschwäche in Ihrer Anwendung. Zum Testen ist es besser, so etwas wie eine Abhängigkeitsinjektion zu verwenden, die eine Substitution ermöglicht. Dann könnten Sie Tests schreiben, die unabhängig von den aktuellen Geschäftsregeln sind.

Ein Nebeneffekt davon ist, dass es für den Geschäftsinhaber häufig einfacher ist (nicht unbedingt für den Unternehmensinhaber, sondern eher für die Person, die in der Unternehmenshierarchie für dieses Produkt verantwortlich ist), die Geschäftsregeln direkt zu konfigurieren. Wenn Sie über ein solches technisches Framework verfügen, ist es einfach, dem Geschäftsinhaber die Verwendung einer Benutzeroberfläche (UI) zur Konfiguration des Angebots zu ermöglichen. Der Geschäftsinhaber würde die Einschränkung in der Benutzeroberfläche auswählen und den markLimitedToGender("M")Aufruf ausgeben . Wenn das Angebot dann in der Datenbank gespeichert wird, wird dies gespeichert. Sie müssten das Angebot jedoch nicht speichern, um es zu verwenden. Ihre Tests könnten also ein Angebot erstellen und konfigurieren, das nicht in der Datenbank vorhanden ist.

In Ihrem System müsste der Geschäftsinhaber, wie beschrieben, eine Anforderung an die technische Gruppe senden, die die entsprechende SQL-Anweisung ausgibt und die Tests aktualisiert. Oder die technische Gruppe muss Ihren Code und Ihre Tests bearbeiten (oder testet dann den Code). Das scheint ein ziemlich schwerer Ansatz zu sein. Du kannst es schaffen. Aber Ihre Software (nicht nur Ihre Tests) wäre weniger spröde, wenn Sie dies nicht tun müssten.

TL; DR : Sie können Tests wie diese schreiben, aber Sie sind möglicherweise besser dran, Ihre Software zu schreiben, damit Sie dies nicht tun müssen.

mdfst13
quelle
Ich habe die Freiheit, einige kleine Details in Ihrer Antwort zu verbessern. Bitte überprüfe, ob ich deine Absichten richtig verstanden habe.
Doc Brown
Im ursprünglichen Beitrag gibt es keinen Hinweis darauf, dass es sich um eine Datenbank handelt. Die Behauptung, dass sich Offer1 bereits in der Datenbank befindet, ist bizarr.
Winston Ewert
2
@ WinstonEwert: es gibt einen klaren hinweis, du musst die frage genauer durchlesen. Ich habe es auch beim ersten Lesen nicht bemerkt, aber es ist in der Tat das, worüber das OP spricht.
Doc Brown
@ mdfst13, tut mir leid, dass ich das verpasst habe. Das OP sagt jedoch, dass sich die Bedingungen manchmal in einer Datenbank und manchmal im Domänenmodell befinden. Ihre Antwort ist für den ersten Fall vollkommen in Ordnung und für den zweiten völlig respektlos. Wenn Sie Ihre Antwort bearbeiten, um diesen Punkt zu klären, werde ich meine hastige Ablehnung entfernen.
Winston Ewert