[JAVA] Benötige ich einen Test, wenn ich DDD in einer Sprache mit einem Typ mache?

Bei einer früheren DDD-Diskussionsrunde wurde ich gefragt: "Wenn Sie DDD in einer statischen Sprache ausführen, benötigen Sie einen Test?", Und mehr Menschen waren überrascht, "Ja / Vielleicht" zu hören. Also habe ich beschlossen, es ein wenig zu klären.

Zusammenfassend lautet meine Theorie: "Natürlich hat das Kompilieren viele Vorteile, aber das Aufhören des Denkens löst nicht alles, und es wird viel gelernt, daher ist es besser, es zu schreiben."

Die bedingte Verzweigung ist anfällig für Fehler, selbst wenn sie einen Typ hat

Zum Beispiel ist eine Änderung, die "else if" erhöht, ziemlich gefährlich, wenn es nur einen Typ gibt.

Plan.java


public enum Plan {
    PLAN_1,
    PLAN_2,
    PLAN_3;
}

Price.java


public class Price {
    private final int value;

    public static Price of(Plan plan) {
        if (plan == Plan.PLAN_1)
            return new Price(100);

        else if (plan == Plan.PLAN_2)
            return new Price(200);

        else
            return new Price(300);
    }
}

Ich habe "PLAN_4" hinzugefügt.

Ich wollte, dass der Preis 400 Yen beträgt, aber ich habe vergessen, "else if" hinzuzufügen.

public enum Plan {
    PLAN_1,
    PLAN_2,
    PLAN_3,
+   PLAN_4;
}

Natürlich geht die Zusammenstellung durch und PLAN_4 kostet 300 Yen.

erschrocken. sehr gruselig.

Beiseite

Es ist ein dummes Beispiel, aber es ist kein Witz.

Es ist nicht ungewöhnlich, dass Produkte daran denken, "else if" wie "price", "detail item", "mail text", "system code" usw. gemäß "Plan" hinzuzufügen. ** Es ist ziemlich rücksichtslos, diese Art von Leckage in großem Maßstab zu verhindern. ** ** **

Was machst du dann

Das Schreiben eines Tests sollte etwas besser sein.

Es kann festgestellt werden, ob alle Elemente von enum (Plan.values ()) im Testcode gut verflochten sind.

PriceTest.groovy


//Der Testcode verwendete Groovy und Spock

class PriceTest extends Specification {
    def "#plan -> #exp"() {
        expect:
        Price.of(plan) == new Price(exp)

        where:
        plan        || exp
        Plan.PLAN_1 || 100
        Plan.PLAN_2 || 200
        Plan.PLAN_3 || 300
    }

    def guard() {
        expect:
        Plan.values().length == 3
    }
}

Es ist einfach, aber wenn ein Test, der von "Plan" abhängt, "== 3" enthält, werden Sie feststellen, wann "Plan" zunimmt oder abnimmt.

Ich benutze "else" nicht für die Preisbeurteilung.

Price.java


        else if (plan == Plan.PLAN_3)
            return new Price(300);
        
        else
            throw new RuntimeException("match error");

Wenn Sie Testcode haben, werden Sie ihn bemerken.

Organisieren: Ich habe hier Angst

Argumentbetrug kann von Vorteil sein

Im Gegenteil, wenn Sie es gut machen, können Sie solche Fehler perfekt verhindern.

Beispiel 1

MailService.java


public class MailService {
    public void send(String itemName, String userName) {
        String mailBody = MailBodyFactory.create(itemName, userName);
        ...
    }
}

Beispiel 2

FooService.java


public class FooService {
    FooRepository fooRepository;

    public void replace(Foo usingOne) {
        Foo newOne = Foo.newOne();
        
        fooRepository.replace(newOne, usingOne);
    }
}

Was falsch ist, ist ...

Beispiel 1

MailFactory.java


public class MailBodyFactory {
    public static String create(String userName, String itemName) {
        return String.format("%s%Vielen Dank für den Kauf von s", userName, itemName);
    }
}

Beispiel 2

FooRepository.java


public interface FooRepository {
    void replace(Foo usingOne, Foo newOne);
}

Die Reihenfolge der Argumente wurde umgekehrt.

Noch eine beiseite

Hast du gedacht "Weißt du!"? Aber die meiste Zeit erinnere ich mich nicht an die Reihenfolge der Produktcode-Argumente, und es ist unerwartet anstrengend, sie sorgfältig zu implementieren.

Außerdem ist so etwas sehr schlecht, weil es sich schlecht bewegt.

Was machst du dann

Missbrauche String nicht und erstelle Klassen für UserName und ItemName. Dann kann man es nie umkehren.

Foo scheint eine Domain-Klasse erstellt zu haben, aber teilen wir sie zum Beispiel durch den Status.

public interface FooRepository {
-   void replace(Foo usingOne, Foo newOne);
+   void replace(UsingFoo usingFoo, NewFoo newFoo);
}

Dies wird niemals das Gegenteil sein. (Ich habe diesen Artikel zusammen mit dem Impuls über die Klassen nach Status geschrieben → [Stoppen wir die göttliche Entität, die alle Status in einer Klasse ausdrückt!](Https://qiita.com/suzuki-hoge/items / d3ea5940898d85bbc03e)))

Es ist eine grobe Anleitung, aber ich denke, es ist besser, ** einzugeben, wo Sie versuchen, mit Variablennamen zu erklären **.

Es ist viel einfacher, die rote Linie zu bekommen, und der Herausgeber denkt auch klug über Komplementkandidaten nach.

Übrigens habe ich schon einmal eine ähnliche Geschichte geschrieben. Wenn Sie also interessiert sind, schauen Sie bitte auch dort nach. Es fühlt sich gut an, unterschiedliche Klassen für dieselben Tabellenwerte zu haben

Organisieren: Ich habe hier Angst

Die Verwendung ist möglicherweise schwierig, wenn Sie einen Test haben

Zum Beispiel dieser Code

CampaignCode.java


public enum CampaignCode {
    CODE_A, CODE_B;

    public static CampaignCode create() {
        if (LocalDateTime.now().getDayOfMonth() < 15) {
            return CODE_A;
        } else {
            return CODE_B;
        }
    }
}

Wenn ich versuche, einen Testcode zu schreiben, stört "jetzt" und ich sollte sagen "Oh, ich kann keinen Test schreiben ... ??".

Dies bedeutet, dass das von Ihnen hergestellte Produkt nur mit "jetzt" funktioniert, sodass es das Testen anderer Teile stört und bei Verspottungs- und Integrationstests nicht flexibel ist.

** Der Testcode verwendet zuerst diese Methode **, daher ist es eine gute Chance, so etwas wie "das? Ist diese Methode schwierig zu verwenden?" Zu bemerken.

(In diesem Beispiel sollte "jetzt" als Argument übergeben werden.)

Zusammenfassung

abschließend

Im Fall von Enum treten beispielsweise Warnungen und Kompilierungsfehler in Abhängigkeit von der Sprache auf, aber ich dachte, dass der Hauptfluss des Veranstaltungsortes "Beseitigt die Kompilierung die Notwendigkeit von Testcode?". Also dachte ich darüber nach, ihn als Antithese zu schreiben. Ich tat.

Dieser Artikel ist meine persönliche Theorie.

Wenn Sie die Worte einer Person imitieren möchten, die Sie kürzlich gehört haben, wenn Sie denken "Ich habe gehört, Sie brauchen keinen Test, oder?", Schreiben Sie den Test gehorsam. Ich denke. Das ist es.

Recommended Posts

Benötige ich einen Test, wenn ich DDD in einer Sprache mit einem Typ mache?
Ich habe ein Programm zur Beurteilung von Primzahlen in Java erstellt
Ich habe ein Programm zur Beurteilung von Primzahlen in Java geschrieben
Ausgabe true mit if (a == 1 && a == 2 && a == 3) in Java (Invisible Identifier)
Ich habe jetzt einen Test mit Spring Boot + JUnit 5 geschrieben
Ich möchte das Flash-Attribut im Frühjahr, auch wenn ich einen Reverse-Proxy festgelegt habe! (TU es nicht)
Was tun, wenn im Testcode der Steuereinheit in Rails der Fehler "302" angezeigt wird?
Ich habe einen CRUD-Test mit SpringBoot + MyBatis + DBUnit geschrieben (Teil 1)
Was tun, wenn in GlassFish eine java.io.IOException auftritt?
Ich habe mit Gem in Ruby nach einem Webframework gesucht
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben
Ich bin auf einen Typen mit zwei Punkten in Rails gestoßen
Was tun, wenn in Docker ein gcc-Fehler auftritt?
Was ist, wenn ich eine finally-Klausel in die Try-with-Resources-Syntax schreibe?
Ich kann in IntelliJ keine Java-Klasse mit einem bestimmten Namen erstellen
Erstellen Sie ein Eltern-Kind-Beziehungsformular mit form_object (ich habe auch einen Test geschrieben)
Ich habe kürzlich eine JS-App in der gemunkelten Dart-Sprache erstellt
Ich habe ein PDF mit Java erstellt.
Ich habe mit Swing eine GUI erstellt
Was tun, wenn Sie in Thymeleaf Layout eine groovige Warnung erhalten?
Ich möchte im Dialogfeld mehrere Elemente mit einem benutzerdefinierten Layout auswählen
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben (PowerMockito Edition)
Ich kann nicht bauen, wenn ich das Build-Ziel mit XCode12 auf einen Simulator setze!
Ich wollte eine Diashow mit Slick modisch umsetzen.
Ich möchte ein chinesisches (koreanisches) PDF mit dünnen Berichten anzeigen
Ich habe eine Lambda-Funktion in Java geschrieben und mit SAM bereitgestellt
Ich möchte für jedes Array mit Lambda-Ausdruck in Java