Ceci est une ancienne révision du document !
Tests Spring
Dépendances pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
< 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 > < dependency > < groupId >org.seleniumhq.selenium</ groupId > < artifactId >selenium-java</ artifactId > < scope >test</ scope > </ dependency > < dependency > < groupId >io.github.bonigarcia</ groupId > < artifactId >webdrivermanager</ artifactId > < version >5.3.1</ version > < scope >test</ scope > </ dependency > </ dependencies > |
@WebMvcTest
Tester un composant (Controller, service…)
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@SpringBootTest (webEnvironment = WebEnvironment.RANDOM_PORT) class HttpRequestTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test void greetingShouldReturnDefaultMessage() throws Exception { String. class )).contains( "Hello World!" ); } } |
@SpringBootTest & @AutoConfigureMockMvc
Mock du serveur web, remplacé par MockMvc :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
@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); 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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
< 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 > |