[JAVA] Testen Sie die Klasse mit Feldinjektion im Spring-Boot-Test, ohne den Spring-Container zu verwenden

Wenn Sie Erfahrung mit Java haben, ist das ganz natürlich, aber ich denke, dass es Leute gibt, die die gleichen Probleme haben wie ich und das Gefühl haben, "Oh, das ist richtig", also werde ich es schreiben. Es ist nur eine Reflexionsgeschichte. Ob diese Methode als Testdesign richtig oder falsch ist, ist jedoch unterschiedlich und wird daher nur als eine der Methoden beschrieben. (Es besteht auch die Möglichkeit eines Anti-Patterns!) Ich habe es geschrieben, aber es ist auch die Methode, die mich dazu inspiriert hat, in Zukunft mit Konstruktorinjektion zu entwerfen.

Probleme beim Testen mit Spring-Funktionen (Container)

In dem Projekt, an dem ich arbeite, dauert es ungefähr 3 Minuten (auf dem ausgeliehenen PC), um verschiedene Informationen zum Starten der App zu laden, und der -Test wird nur langsam ausgeführt. </ b> Ich wollte die Komponententests mit gutem Rhythmus durchlaufen, aber diese Zeit wurde für jeden Test mit dem Federbehälter verwendet, was aus dem Grund ausreichte, den ich nicht testen wollte.

Wenn Sie beispielsweise den Spring-Container im Test wie folgt aufrufen (Spring boot ver 1.X)

Getestete Klasse


@Service
public class ParentService {

    @Autowired
    ChildService childService;//Feldinjektion

    public SampleForm generateSample(int n) {
        //Hier injizieren
        int calNumber = childService.calculate(n);
        return this.prepared(calNumber);
    }

    private SampleForm prepared(int calNumber) {
        int newN = calNumber * 2;
        SampleForm sample = new SampleForm();
        sample.setCalNumber(newN);
        return sample;
    }

}

Prüfung


@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfig.class)
@WebIntegrationTest(randomPort = true)
@Transactional
public class SampleTest {
    
    @Autowired
    ParentService parentService;

    @Test
Wenn Sie der Methode "public void generate" 2 geben, erhalten Sie eine SampleForm mit 4.() {
        SampleForm sample = parentService.generateSample(2)
        assertThat(sample.getCalNumber(), is(4));
    }
}

Ich werde es gerne machen Zum Beispiel reicht es nicht aus, jedes Mal ein paar Minuten zu warten, wenn Sie einen so kurzen Test durchführen.

Bisher zum Beispiel im obigen Fall "ParentService.prepared" Machen Sie die Methode öffentlich </ b>


//Verwenden Sie nicht die Federbehälterfunktion.
public class SampleTest {
    
    ParentService parentService = new ParenteService();

    @Test
Wenn Sie der öffentlich nichtig vorbereiteten Methode 2 geben, erhalten Sie eine SampleForm mit 4.() {
        SampleForm sample = parentService.prepared(2)
        assertThat(sample.getCalNumber(), is(4));
    }
}

Ich habe versucht, das Testen mit POJO zu ermöglichen, indem ich es so weit wie möglich gemacht habe, die Feldinjektionsmethode nicht innerhalb der Methode aufgerufen und das abhängige Objekt als Argument übergeben habe. Die durch Feldinjektion aufgerufene Klasse wird nur injiziert, wenn der Spring-Container verwendet wird. Daher wird das neue POJO auf null gesetzt. Wenn Sie wie folgt schreiben

ParentService.childeService wird in generateSample aufgerufen.NullPointerException tritt beim Caluclieren auf.



```java

public class SampleTest {
    
    ParentService parentService = new ParentService();

    @Test
Wenn Sie der Methode "public void generate" 2 geben, erhalten Sie eine SampleForm mit 4.() {
        /*
childService in der generateSample-Methode.Anruf berechnen, aber in diesem Test
        new ParentService()Da childService null ist, tritt eine Ausnahme auf.
        */
        SampleForm sample = parentService.generateSample(2)//Exception!      
        assertThat(sample.getCalNumber(), is(4));
    }
}

ChildService.Die Caluclate-Methode ist auch öffentlich, in einem anderen Test validiert und ParentService.Wenn vorbereitet, kann dies auch durch Tests überprüft werden



#### **`  Parentservice.Da generateSample korrekt sein sollte, denke ich, dass es ohne Test korrekt sein sollte (im einfachen Beispiel oben). Als ursprüngliches Design jedoch ParentService.vorbereitet ist keine Methode, um von außen aufzurufen,<b>Ist es wirklich richtig, es zum Testen öffentlich zu machen? Ich fühle mich anders.</b>`**

Da es sich in der Realität nicht um eine so einfache Implementierung handelt, möchten Sie möglicherweise eine Methode testen, bei der ein Objekt durch Feldinjektion injiziert wurde.

Ich dachte plötzlich dort. "In einer dynamischen Sprache können Sie dynamisch auf Felder zugreifen und diese festlegen und Methoden dynamisch definieren ... Nun, Java hat auch Reflexionen." In der Sprache Java berühren wir Felder normalerweise nicht dynamisch. In JavaScript ist es natürlich, dynamisch auf Feldnamen zu verweisen und Methoden zu Objekten hinzuzufügen, aber in der Java-Entwicklung ist es normalerweise (meine flache) Aufgabe, dies für Produktionscode zu tun. Ich denke nicht (meines Wissens). Ich denke, es gibt verschiedene Gründe, aber ich verstehe, dass ein Merkmal von Java darin besteht, dass statische Typisierung die Sicherheit beeinträchtigt. Ich hatte also keine Idee, Reflexion zu verwenden, aber ich dachte, es könnte verwendet werden, um die obigen Tests durchzuführen.

Und ich schrieb wie folgt.


public class SampleTest {
    
    
    ParentService parentService = new ParentService();

    @Before
    public void setUp() {
        Class<?> c = parentService.getClass();
        parentService = (ParentService)c.newInstance();
        Field f = c.getDeclaredField("childService");
        f.setAccessible(true);
        f.set(parentService , new ChildService());

    }

    @Test
Wenn Sie der Methode "public void generate" 2 geben, erhalten Sie eine SampleForm mit 4.() {
        /*
childService in der generateSample-Methode.Anruf berechnen. Mit Reflexion
Die folgenden Tests sind möglich, da sie eingestellt sind.
        */
        SampleForm sample = parentService.generateSample(2)        
        assertThat(sample.getCalNumber(), is(4));
    }
}

Wenn Sie wie oben beschrieben schreiben, können Sie auf das Feld zugreifen und es festlegen, ohne den Spring-Container zu verwenden. Sie können testen. Daher ist es möglich, innerhalb der Zeit bis zum Beginn des Tests zu testen. Im Beispiel wird new übergeben und "ChildService" übergeben, es ist jedoch auch möglich, ein Mock mit Verhalten festzulegen.

Was ist jedoch, wie eingangs erwähnt, überhaupt Injektion? Da ich nur das Wissen über den Rang hatte, dachte ich über die obige Methode nach und beschloss, das Design zu überprüfen, indem ich das Buch über den Frühling zurücklas und den folgenden Blog las.

Referenz Warum Spring die Konstruktorinjektion der Feldinjektion empfiehlt

das ist alles.

Recommended Posts