Tests Spring
Dépendances pom.xml
<dependencies> ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/io.github.bonigarcia/webdrivermanager --> <dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>5.6.2</version> <scope>test</scope> </dependency> </dependencies>
@WebMvcTest
Tester un composant (Controller, service…)
Controller
@Controller public class HelloController { @Autowired private HelloService helloService; @GetMapping("/hello") public @ResponseBody String helloAction() { return helloService.getMessage(); } @ModelAttribute("message") public String getMessage() { return helloService.getMessage(); } @GetMapping("/hello/view") public String helloViewAction() { return "hello"; } @GetMapping("/auth/hello") public @ResponseBody String authHelloAction() { return helloService.getAuthMessage(); } @GetMapping("/hello/js/{msg}") public String helloWithJSAction(@PathVariable String msg) { return "helloJs"; } }
Test
Mocking :
- Serveur : @MockMvc
- Service HelloService : @MockBean
@WebMvcTest(HelloController.class) @ContextConfiguration(classes = {WebSecurityConfig.class, SpringTestsApplication.class}) class HelloControllerTest { @MockBean private HelloService helloService; @Autowired private MockMvc mockMvc; @Test void helloShouldReturnBonjour() throws Exception { // Given when(helloService.getMessage()).thenReturn("Bonjour"); // When ResultActions results = this.mockMvc.perform(MockMvcRequestBuilders.get("/hello")); // Then results.andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(content().string(containsString("Bonjour"))); } @Test void helloViewShouldReturnBonjour() throws Exception { // Given when(helloService.getMessage()).thenReturn("Bonjour"); // When ResultActions results = this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/view")); // Then results.andExpect(view().name("hello")).andExpect(model().attribute("message", "Bonjour")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(content().string(containsString("Bonjour"))); } }
Tests d'intégration
Test d'intégration avec lancement du serveur sur Random port (pour éviter les conflits).
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class HttpRequestTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test void greetingShouldReturnDefaultMessage() throws Exception { assertThat(this.restTemplate.getForObject("http://127.0.0.1:" + port + "/", String.class)).contains("Hello World!"); } }
@SpringBootTest & @AutoConfigureMockMvc
Mock du serveur web, remplacé par MockMvc :
@SpringBootTest @AutoConfigureMockMvc @AutoConfigureTestDatabase(replace = Replace.NONE) class RestUserControllerTest { @Autowired private MockMvc mockMvc; @Autowired private UserService userService; private static User testUser; @BeforeEach public void setup() { testUser = userService.createUser("Bob", "Duke"); } @AfterEach public void tearDown() { userService.deleteAll(); } @Test void getAllShouldReturnAllUsers() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.get("/rest/users")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.size()").value(1)) .andExpect(MockMvcResultMatchers.jsonPath("$[0].firstname").value("Bob")) .andExpect(MockMvcResultMatchers.jsonPath("$[0].lastname").value("Duke")); } }
Test sécurité
Authentification
Autorisations
Mocking :
- Serveur web MockMvc
- Roles/Users @WithMockUser @WithAnonymousUser
@SpringBootTest @AutoConfigureMockMvc class SecureApplicationTests { @Autowired private MockMvc mockMvc; @Test @WithMockUser("admin") void authHelloWithAdminShouldReturnAdmin() throws Exception { this.mockMvc.perform(get("/auth/hello")).andDo(print()).andExpect(status().isOk()) .andExpect(content().string(containsString("admin"))); } @Test @WithAnonymousUser void authHelloWithAnonymousUserShouldReturn401() throws Exception { this.mockMvc.perform(get("/auth/hello")).andDo(print()).andExpect(status().isUnauthorized()); } }
Selenium tests
Tests du comportement côté client
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class SeleniumDemoTest { private WebDriver driver; @LocalServerPort int randomServerPort; String baseUrl; @SuppressWarnings("deprecation") @BeforeEach void setUp() throws Exception { WebDriverManager.chromedriver().setup(); ChromeOptions options = new ChromeOptions(); options.addArguments("--no-sandbox"); options.addArguments("--disable-dev-shm-usage"); options.addArguments("--headless"); driver = new ChromeDriver(options); baseUrl = "http://127.0.0.1:" + randomServerPort; navigateTo("/hello"); driver.manage().window().maximize(); driver.manage().timeouts().implicitlyWait(120, TimeUnit.MILLISECONDS); } @AfterEach void tearDown() throws Exception { if (driver != null) { driver.quit(); } } private void navigateTo(String relativeURL) { driver.navigate().to(baseUrl + relativeURL); } private void fillElement(String name, String content) { WebElement elm = driver.findElement(By.name(name)); elm.sendKeys(content); } private void btnClick(String cssSelector) { driver.findElement(ByCssSelector.cssSelector(cssSelector)).click(); } private void assertElementContainsText(String cssSelector, String text) { assertTrue(driver.findElement(ByCssSelector.cssSelector(cssSelector)).getText().contains(text)); } private void assertElementAttributeContainsText(String cssSelector, String attribute, String text) { assertTrue(driver.findElement(ByCssSelector.cssSelector(cssSelector)).getAttribute(attribute) .contains(text)); } public void waitForTextToAppear(String textToAppear, WebElement element, int timeout) { WebDriverWait wait = new WebDriverWait(driver, Duration.ofMillis(timeout)); wait.until(ExpectedConditions.textToBePresentInElement(element, textToAppear)); } public void waitForTextToAppear(String textToAppear, WebElement element) { waitForTextToAppear(textToAppear, element, 3000); } @Test void helloRouteShouldReturnBonjour() { assertTrue(driver.getCurrentUrl().contains("hello")); assertElementContainsText("body", "Bonjour"); } @Test void helloWithJsRouteShouldReturnLength() { String msg = "Bonjour"; navigateTo("/hello/js/" + msg); assertTrue(driver.getCurrentUrl().contains("/hello/js/" + msg)); assertElementAttributeContainsText("#msg", "value", msg); btnClick("#bt"); assertElementContainsText("#length", msg.length() + ""); } }
Couverture
Intégration de Jacoco :
<build> <plugins> ... <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.11</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>prepare-package</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build>