Test Controller trong dự án Spring Boot

Ở bài trước mình đã hướng dẫn các bạn viết test cho Module Service. So với service, mình không cần thêm thư viện khi test Controller. Nếu dự án của mình có sử dụng bảo mật và phân quyền thì mình thêm thư viện này các bạn nhé. Mà dự án nào chẳng có phân quyền nhỉ -)))

Nói chung mình thêm thư viện này:

    testImplementation 'org.springframework.security:spring-security-test'

Giờ mình nói qua về test controller 1 chút nhé. Module Controller nhận dữ liệu từ Module Service. Nếu mình viết unit test thì mình chỉ cần kiểm tra xem service dùng ở controller có trả về dữ liệu đúng hay không. Giả sử mình muốn test phương thức getAllCategories trong Controller.

 @GetMapping("/categories") 
public ResponseEntity<Iterable<Categories>> getAllCategories(){
        return new ResponseEntity<>(categoryService.findAll(), HttpStatus.OK);
    }

Nếu mình viết unit test thì mình chỉ cần làm tương tự như test service theo từng bước:

  • Làm giả CategoryService
  • Nói cho Service vừa được làm giả kết quả trả về tương ứng
  • So sánh kết quả trả về với kết quả mong muốn

Tuy nhiên, ở đây chúng tôi không làm thế. Chúng ta cần tạo ra môi trường MVC giả, tạo ra một cổng giả để tạo ra một request face cho việc test ví dụpostface:/categories

Spring Boot hỗ trợ luôn cho chúng ta cách tạo ra cổng đó các bạn nhé.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

Chúng ta để đoạn mã trên ở phía trên phần khởi tạo class Test nha các bạn.

Tiếp tục mình cần tạo ra một MVC giả nữa các bạn ạ. Cái này dùng để làm gì nhỉ? Các bạn tưởng tượng, test Controller mình phải kiểm tra xem mình gửi request gì? mình đang ở role nào? mình được trả về kết quả thế nào? chứ đâu phải là mình gọi phương thức xong mình đi so sánh kết quả như unit test ở service đâu đúng ko?

MockMvc nó sẽ làm điều đó cho mình để đảm bảo mình không cần dùng trình duyệt hoặc Postman để làm điều này.

private MockMvc mvc;

Trong class test các bạn cần đến service nào thì mình tiêm vào nhé. Trong controller này của mình mình thêm

@Autowired
private ICategoryService categoryService;

@Autowired
private ISkillService skillService;

Ở đây mình có BeforEach như sau các bạn nhé.

@BeforeEach
private void init(){
    //
    // Mình khởi tạo các đối tượng ở đây nhé.
    //
    mvc = MockMvcBuilders
            .webAppContextSetup(applicationContext)
            .apply(springSecurity())
            .build();
}

Phần MockMvcBuilders mình thêm phần applicationContext và thêm springSecurity của thư viện sau để mình dựng được 1 MVC giả các bạn nhé.


import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;

Giờ chúng ta bắt đầu viết Test thôi nào.

Mình test phương thức findAll trước nhé. Nội dung của phương thức này như sau:

@GetMapping("/categories")
public ResponseEntity<Iterable<Categories>> getAllCategories(){
return new ResponseEntity<>(categoryService.findAll(), HttpStatus.OK);
}

Mình phân quyền cho nó là có 2 role AMIN và COACH có thể request được đường dẫn là /categories. Vì vậy ở đây mình sẽ test 2 trường hợp.

TH1: Với role ADMIN sẽ trả về HTTPStatus OK.

@Test
@WithMockUser(username = "admin", roles = "ADMIN")
@DisplayName("find all return status 200 with role admin")
void findAll_whenGetCategoriesWithRoleAdmin_thenReturnStatus200() throws Exception {
mvc.perform(get("/categories").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}

TH2: Tương tự với role Coach.

@Test
@WithMockUser(username = "coach", roles = "COACH")
void whengetCategoriesWithRoleCoach_thenReturnStatus200() throws Exception {
mvc.perform(get("/categories").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *