Mamy kilka przypadków testowych JUnit (testy integracyjne), które są logicznie pogrupowane w różne klasy testowe.
Jesteśmy w stanie załadować kontekst aplikacji Spring raz na klasę testową i ponownie użyć go we wszystkich przypadkach testowych w klasie testowej JUnit, jak wspomniano w http://static.springsource.org/spring/docs/current/spring-framework-reference /html/testing.html
Jednak zastanawialiśmy się tylko, czy istnieje sposób, aby załadować kontekst aplikacji Spring tylko raz dla kilku klas testowych JUnit.
FWIW, używamy Spring 3.0.5, JUnit 4.5 i używamy Mavena do budowy projektu.
spring
junit
junit4
spring-test
Ramesh
źródło
źródło
Odpowiedzi:
Tak, jest to całkowicie możliwe. Wszystko, co musisz zrobić, to użyć tego samego
locations
atrybutu w swoich klasach testowych:@ContextConfiguration(locations = "classpath:test-context.xml")
Spring buforuje konteksty aplikacji według
locations
atrybutu, więc jeśli ten samlocations
pojawia się po raz drugi, Spring używa tego samego kontekstu zamiast tworzyć nowy.Napisałem artykuł o tej funkcji: Przyspieszenie testów integracji Spring . Jest to również szczegółowo opisane w dokumentacji Springa: 9.3.2.1 Zarządzanie kontekstem i buforowanie .
Ma to interesującą konsekwencję. Ponieważ Spring nie wie, kiedy JUnit zostanie ukończony, buforuje cały kontekst na zawsze i zamyka go za pomocą haka zamykającego JVM. Takie zachowanie (zwłaszcza gdy masz wiele klas testowych z różnymi
locations
) może prowadzić do nadmiernego użycia pamięci, wycieków pamięci itp. Kolejna zaleta buforowania kontekstu.źródło
Aby dodać do odpowiedzi Tomasza Nurkiewicza , od wiosny 3.2.2
@ContextHierarchy
adnotacja może mieć oddzielną, skojarzoną strukturę wielu kontekstów. Jest to przydatne, gdy wiele klas testowych chce współużytkować (na przykład) konfiguracje bazy danych w pamięci (źródło danych, EntityManagerFactory, menedżer tx itp.).Na przykład:
@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 { ... }
Mając tę konfigurację, kontekst używający „test-db-setup-context.xml” zostanie utworzony tylko raz, ale elementy bean wewnątrz niego mogą zostać wstrzyknięte do kontekstu pojedynczego testu jednostkowego
Więcej informacji na temat podręcznika: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (wyszukaj „ hierarchię kontekstu ”)
źródło
Zasadniczo spring jest wystarczająco inteligentny, aby skonfigurować to za Ciebie, jeśli masz taką samą konfigurację kontekstu aplikacji w różnych klasach testowych. Na przykład, powiedzmy, że masz dwie klasy A i B w następujący sposób:
@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... }
W tym przykładzie klasa A naśladuje fasolę C, podczas gdy klasa B symuluje fasolę D.W związku z tym spring traktuje je jako dwie różne konfiguracje i w ten sposób ładuje kontekst aplikacji raz dla klasy A i raz dla klasy B.
Jeśli zamiast tego chcielibyśmy, aby wiosna współużytkowała kontekst aplikacji między tymi dwiema klasami, musiałyby wyglądać następująco:
@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... }
Jeśli połączysz swoje klasy w ten sposób, spring załadowałby kontekst aplikacji tylko raz, albo dla klasy A, albo B, w zależności od tego, która z tych dwóch klas zostanie uruchomiona jako pierwsza w zestawie testów. Można to powielić w wielu klasach testowych, jedynym kryterium jest to, że nie należy dostosowywać klas testowych w inny sposób. Wszelkie dostosowania, które powodują, że klasa testowa różni się od innych (w oczach wiosny), kończyłyby się wiosną utworzeniem innego kontekstu aplikacji.
źródło
utwórz swoją klasę konfiguracji, jak poniżej
@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); }
Utwórz swoje klasy testowe, jak poniżej
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 } }
źródło
Godnym uwagi jest to, że jeśli użyjemy @SpringBootTests, ale ponownie
use @MockBean in different test classes
, Spring nie ma możliwości ponownego wykorzystania kontekstu aplikacji we wszystkich testach.Rozwiązaniem jest
to move all @MockBean into an common abstract class
i to rozwiązuje problem.@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class) public abstract class AbstractIT { @MockBean private ProductService productService; @MockBean private InvoiceService invoiceService; }
Następnie klasy testowe można zobaczyć jak poniżej
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() { } }
źródło