Utilizzo di SpringJUnit4ClassRunner con Parameterized

1. Panoramica

In questo tutorial, vedremo come parametrizzare un test di integrazione Spring implementato in JUnit4 con un test runner JUnit parametrizzato .

2. SpringJUnit4ClassRunner

SpringJUnit4ClassRunner è un'implementazione di ClassRunner di JUnit4 che incorpora TestContextManager di Spring in un test JUnit .

TestContextManager è il punto di ingresso nel framework Spring TestContext e quindi gestisce l'accesso a Spring ApplicationContext e l'inserimento delle dipendenze in una classe di test JUnit. Pertanto, SpringJUnit4ClassRunner consente agli sviluppatori di implementare test di integrazione per componenti Spring come controller e repository.

Ad esempio, possiamo implementare un test di integrazione per il nostro RestController :

@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerIntegrationTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; private static final String CONTENT_TYPE = "application/text;charset=ISO-8859-1"; @Before public void setup() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test public void givenEmployeeNameJohnWhenInvokeRoleThenReturnAdmin() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders .get("/role/John")) .andDo(print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE)) .andExpect(MockMvcResultMatchers.content().string("ADMIN")); } }

Come si può vedere dal test, il nostro Controller accetta un nome utente come parametro di percorso e restituisce il ruolo utente di conseguenza.

Ora, per testare questo servizio REST con una diversa combinazione nome utente / ruolo, dovremmo implementare un nuovo test:

@Test public void givenEmployeeNameDoeWhenInvokeRoleThenReturnEmployee() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders .get("/role/Doe")) .andDo(print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE)) .andExpect(MockMvcResultMatchers.content().string("EMPLOYEE")); }

Ciò può sfuggire rapidamente di mano per i servizi in cui è possibile un gran numero di combinazioni di input.

Per evitare questo tipo di ripetizione nelle nostre classi di test, vediamo come utilizzare Parameterized per implementare test JUnit che accettano più input.

3. Utilizzando parametrizzato

3.1. Definizione dei parametri

Parameterized è un test runner JUnit personalizzato che ci consente di scrivere un singolo test case e di eseguirlo su più parametri di input:

@RunWith(Parameterized.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerParameterizedIntegrationTest { @Parameter(value = 0) public String name; @Parameter(value = 1) public String role; @Parameters public static Collection data() { Collection params = new ArrayList(); params.add(new Object[]{"John", "ADMIN"}); params.add(new Object[]{"Doe", "EMPLOYEE"}); return params; } //... }

Come mostrato sopra, abbiamo utilizzato l' annotazione @Parameters per preparare i parametri di input da inserire nel test JUnit. Abbiamo anche fornito la mappatura di questi valori nel nome e nel ruolo dei campi @Parameter .

Ma ora, abbiamo un altro problema da risolvere: JUnit non consente più corridori in una classe di test JUnit . Ciò significa che non possiamo sfruttare SpringJUnit4ClassRunner per incorporare TestContextManager nella nostra classe di test. Dovremo trovare un altro modo per incorporare TestContextManager .

Fortunatamente, Spring offre un paio di opzioni per raggiungere questo obiettivo. Ne discuteremo nelle sezioni seguenti.

3.2. Inizializzazione manuale di TestContextManager

La prima opzione è abbastanza semplice, poiché Spring ci consente di inizializzare TestContextManager manualmente:

@RunWith(Parameterized.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerParameterizedIntegrationTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; private TestContextManager testContextManager; @Before public void setup() throws Exception { this.testContextManager = new TestContextManager(getClass()); this.testContextManager.prepareTestInstance(this); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } //... }

In particolare, in questo esempio, abbiamo utilizzato il runner parametrizzato invece di SpringJUnit4ClassRunner. Successivamente, abbiamo inizializzato TestContextManager nel metodo setup () .

Ora possiamo implementare il nostro test JUnit parametrizzato:

@Test public void givenEmployeeNameWhenInvokeRoleThenReturnRole() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders .get("/role/" + name)) .andDo(print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE)) .andExpect(MockMvcResultMatchers.content().string(role)); }

JUnit eseguirà questo test case due volte , una per ogni set di input che abbiamo definito utilizzando l' annotazione @Parameters .

3.3. SpringClassRule e SpringMethodRule

In genere, non è consigliabile inizializzare manualmente TestContextManager . Invece, Spring consiglia di utilizzare SpringClassRule e SpringMethodRule.

SpringClassRule implementa TestRule di JUnit, un modo alternativo per scrivere casi di test. TestRule può essere utilizzato per sostituire le operazioni di configurazione e pulizia precedentemente eseguite con i metodi @Before, @BeforeClass, @After e @AfterClass .

SpringClassRule incorpora la funzionalità a livello di classe di TestContextManager in una classe di test JUnit. Inizializza TestContextManager e richiama l'installazione e la pulizia di Spring TestContext. Pertanto, fornisce l'inserimento delle dipendenze e l'accesso a ApplicationContext .

Oltre a SpringClassRule , dobbiamo usare anche SpringMethodRule . che fornisce la funzionalità a livello di istanza e di metodo per TestContextManager.

SpringMethodRule è responsabile della preparazione dei metodi di prova. Controlla anche gli scenari di test contrassegnati per essere saltati e ne impedisce l'esecuzione.

Vediamo come utilizzare questo approccio nella nostra classe di test:

@RunWith(Parameterized.class) @WebAppConfiguration @ContextConfiguration(classes = WebConfig.class) public class RoleControllerParameterizedClassRuleIntegrationTest { @ClassRule public static final SpringClassRule scr = new SpringClassRule(); @Rule public final SpringMethodRule smr = new SpringMethodRule(); @Before public void setup() throws Exception { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } //... }

4. Conclusione

In questo articolo, abbiamo discusso due modi per implementare i test di integrazione Spring utilizzando il test runner con parametri invece di SpringJUnit4ClassRunner . Abbiamo visto come inizializzare TestContextManager manualmente e abbiamo visto un esempio utilizzando SpringClassRule con SpringMethodRule , l'approccio consigliato da Spring.

Sebbene in questo articolo abbiamo discusso solo del runner parametrizzato , possiamo effettivamente utilizzare uno di questi approcci con qualsiasi runner JUnit per scrivere test di integrazione Spring.

Come al solito, tutto il codice di esempio è disponibile su GitHub.