Verwenden Sie den Kontext der Federanwendung für alle Junit-Testklassen erneut

83

Wir haben eine Reihe von JUnit-Testfällen (Integrationstests), die logisch in verschiedene Testklassen gruppiert sind.

Wir können den Spring-Anwendungskontext einmal pro Testklasse laden und für alle Testfälle in einer JUnit-Testklasse wiederverwenden, wie unter http://static.springsource.org/spring/docs/current/spring-framework-reference angegeben /html/testing.html

Wir haben uns jedoch nur gefragt, ob es eine Möglichkeit gibt, den Spring-Anwendungskontext für eine Reihe von JUnit-Testklassen nur einmal zu laden.

FWIW, wir verwenden Spring 3.0.5, JUnit 4.5 und verwenden Maven, um das Projekt zu erstellen.

Ramesh
quelle
5
Alle Antworten unten sind großartig, aber ich habe keine context.xml. Habe ich meinen Weg in Vergessenheit geraten? Gibt es eine Möglichkeit, dies ohne context.xml zu tun?
März 7/18
2
Hast du die Antwort auf deine Lösung gefunden? Ich habe das gleiche Problem und ich möchte dies mit Anmerkungen und Spring Boot erledigen.
AleksandarT

Antworten:

95

Ja, das ist durchaus möglich. Sie müssen lediglich dasselbe locationsAttribut in Ihren Testklassen verwenden:

@ContextConfiguration(locations = "classpath:test-context.xml")

Spring speichert Anwendungskontexte nach locationsAttributen zwischen. Wenn derselbe locationszum zweiten Mal angezeigt wird, verwendet Spring denselben Kontext, anstatt einen neuen zu erstellen.

Ich habe einen Artikel über diese Funktion geschrieben: Beschleunigen von Spring-Integrationstests . Außerdem wird dies in der Spring-Dokumentation ausführlich beschrieben: 9.3.2.1 Kontextverwaltung und Caching .

Dies hat eine interessante Bedeutung. Da Spring nicht weiß, wann JUnit fertig ist, wird der gesamte Kontext für immer zwischengespeichert und mit dem JVM-Shutdown-Hook geschlossen. Dieses Verhalten (insbesondere wenn Sie viele Testklassen mit unterschiedlichen haben locations) kann zu übermäßiger Speichernutzung, Speicherlecks usw. führen. Ein weiterer Vorteil des Caching-Kontexts.

Tomasz Nurkiewicz
quelle
Ah! Ich habe das nicht bemerkt. Wir verfolgen diesen Ansatz seit langer Zeit und ich habe (fälschlicherweise) die lange Dauer für die Testausführung dem Laden des Frühlingskontexts bei jeder Testklasse zugeschrieben. Wird jetzt sorgfältig prüfen. Vielen Dank.
Ramesh
1
Ich würde eher sagen, dass der Frühling keine Kenntnis über die Ausführungsreihenfolge Ihrer Testfälle hat. Infolgedessen kann nicht festgestellt werden, ob der Kontext später benötigt wird oder entsorgt werden kann.
Philnate
1
Ich sehe nicht ein, wie das tatsächlich wahr sein kann. Eclipse / JUnit verbringt jedes Mal, wenn ich einen Run As / JUnit-Test durchführe, 2 Minuten damit, die Umgebung zu verbessern. Dies würde nicht passieren, wenn etwas zwischengespeichert würde.
user1944491
3
Haben Sie eine Idee, ob dies vollständig über Anmerkungen erfolgen kann, anstatt ein XML für die Kontextdefinition zu verwenden? Ich habe viel darüber im Dokument und hier auf SO gesucht, konnte aber nichts finden, was mich zu der Annahme veranlasst, dass dies nicht möglich ist.
Jean-François Savard
Gilt das nicht, wenn Sie Initialisierer haben? meins wird für jeden Test in der Klasse initialisiert
Kalpesh Soni
26

Um die Antwort von Tomasz Nurkiewicz zu ergänzen , kann ab Frühjahr 3.2.2 die @ContextHierarchyAnnotation verwendet werden, um eine separate, zugeordnete Mehrfachkontextstruktur zu haben. Dies ist hilfreich, wenn mehrere Testklassen (z. B.) speicherinterne Datenbank-Setups (Datenquelle, EntityManagerFactory, TX-Manager usw.) gemeinsam nutzen möchten.

Zum Beispiel:

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("FirstTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstTest {
 ...
}

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("SecondTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondTest {
 ...
}

Mit diesem Setup wird der Kontext, der "test-db-setup-context.xml" verwendet, nur einmal erstellt, aber die darin enthaltenen Beans können in den Kontext einzelner Komponententests eingefügt werden

Mehr zum Handbuch: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (Suche nach " Kontexthierarchie ")

Gerrytan
quelle
Ich habe einen Maven mit mehreren Modulen und versuche, das Einrichten der Datenbank im Servicemodul zu vermeiden (da es bereits mit Tests des Datenzugriffsmoduls geladen ist), und es funktioniert bei mir nicht!
Muhammad Hewedy
5
Das hat bei mir funktioniert! Vielen Dank. Um ganz klar zu sein, ohne die Annotation @ContextHierarchy lädt spring meine Datenbank für jeden Test. Ich benutze den "Klassen" -Parameter: @ContextConfiguration (classes = {JpaConfigTest.class, ...
Brel
5
Haben Sie eine Idee, ob dies vollständig über Anmerkungen erfolgen kann, anstatt ein XML für die Kontextdefinition zu verwenden? Ich habe viel darüber im Dokument und hier auf SO gesucht, konnte aber nichts finden, was mich zu der Annahme veranlasst, dass dies nicht möglich ist.
Jean-François Savard
1
@ Jean-FrançoisSavard Hattest du Glück bei deiner Suche (via annotationsw statt XML)?
Javadev
@javadev Ich hoffe, das ist, was Sie suchen docs.spring.io/spring/docs/current/spring-framework-reference/…
Raviteja Gubba
1

Grundsätzlich ist spring intelligent genug, um dies für Sie zu konfigurieren, wenn Sie dieselbe Anwendungskontextkonfiguration für die verschiedenen Testklassen haben. Angenommen, Sie haben zwei Klassen A und B wie folgt:

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

In diesem Beispiel verspottet Klasse A Bean C, während Klasse B Bean D verspottet. Spring betrachtet diese also als zwei verschiedene Konfigurationen und lädt daher den Anwendungskontext einmal für Klasse A und einmal für Klasse B.

Wenn wir stattdessen möchten, dass spring den Anwendungskontext zwischen diesen beiden Klassen teilt, müssten sie wie folgt aussehen:

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

Wenn Sie Ihre Klassen so verkabeln, lädt spring den Anwendungskontext nur einmal für Klasse A oder B, je nachdem, welche Klasse unter den beiden zuerst in der Testsuite ausgeführt wird. Dies kann über mehrere Testklassen hinweg repliziert werden. Das einzige Kriterium ist, dass Sie die Testklassen nicht anders anpassen sollten. Jede Anpassung, die dazu führt, dass sich die Testklasse (in den Augen des Frühlings) von der anderen unterscheidet, würde bis zum Frühjahr einen anderen Anwendungskontext schaffen.

Saurabh Gour
quelle
0

Erstellen Sie Ihre Konfigurationsklasse wie unten

@ActiveProfiles("local")
@RunWith(SpringJUnit4ClassRunner.class )
@SpringBootTest(classes ={add your spring beans configuration classess})
@TestPropertySource(properties = {"spring.config.location=classpath:application"})
@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(S2BXISINServiceTest.class);


    //auto wire all the beans you wanted to use in your test classes
    @Autowired
    public XYZ xyz;
    @Autowired
    public ABC abc;


    }



Create your test suite like below



@RunWith(Suite.class)
@Suite.SuiteClasses({Test1.class,test2.class})
public class TestSuite extends RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(TestSuite.class);


}

Erstellen Sie Ihre Testklassen wie unten

public class Test1 extends RunConfigration {


  @Test
    public void test1()
    {
    you can use autowired beans of RunConfigration classes here 
    }

}


public class Test2a extends RunConfigration {

     @Test
    public void test2()
    {
    you can use autowired beans of RunConfigration classes here 
    }


}
Dileep Gudla
quelle
0

Ein bemerkenswerter Punkt ist, dass use @MockBean in different test classesSpring , wenn wir @SpringBootTests verwenden, den Anwendungskontext nicht für alle Tests wiederverwenden kann.

Die Lösung ist to move all @MockBean into an common abstract classund das behebt das Problem.

@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class)
public abstract class AbstractIT {

   @MockBean
   private ProductService productService;

   @MockBean
   private InvoiceService invoiceService;

}

Dann können die Testklassen wie folgt gesehen werden

public class ProductControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchProduct_ShouldSuccess() {
   }

}

public class InvoiceControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchInvoice_ShouldSuccess() {
   }

}
Quoc Truong
quelle