Xem toàn bộ series bài viết hướng dẫn xây dựng ứng dụng Questions Management tại đây.
Để xây dựng API thêm mới category trong Core Category Service, có mấy việc chúng ta cần phải làm trước:
Việc đầu tiên đó là chúng ta cần thêm mới một interface tên là CategoryRepository để thao tác với MongoDB server.
Interface này sẽ nằm trong package com.huongdanjava.categoryservice.repository, extend từ interface ReactiveMongoRepository của Spring MongoDB Reactive.
Nội dung của interface CategoryRepository như sau:
1 2 3 4 5 6 7 8 9 |
package com.huongdanjava.categoryservice.repository; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import com.huongdanjava.categoryservice.document.Category; public interface CategoryRepository extends ReactiveMongoRepository<Category, String> { } |
với đối tượng document Category:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.huongdanjava.categoryservice.document; import javax.validation.constraints.NotBlank; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import lombok.Data; @Data @Document public class Category { @Id private String id; @NotBlank private String code; @NotBlank private String name; private String description; } |
Các bạn có thể xem thêm bài viết Reactive REST APIs với Spring Data MongoDB Reactive và Spring WebFlux để tham khảo thêm.
Tiếp theo mình sẽ cấu hình phần kết nối tới MongoDB server.
Hiện tại mình đang sử dụng MongoDB server không phải là authentication mode nên mình chỉ cần khai báo như sau trong tập tin application.properties:
1 |
spring.data.mongodb.uri=mongodb://localhost:27017/qm |
Mình sẽ chạy service này sử dụng port 8082 nên mình sẽ thêm property server.port trong tập tin application.properties như sau:
1 |
server.port=8082 |
OK, mọi thứ chuẩn bị đã xong, bây giờ mình sẽ đi vào phần chính của bài viết này các bạn nhé!
Mình sẽ tạo mới một controller tên là CategoryController với nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 |
package com.huongdanjava.categoryservice; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/category") public class CategoryController { } |
Với khai báo trên, mình đã expose các API cho phần Core Category Service với request URL bắt đầu là “/category”.
Để thao tác với MongoDB server, chúng ta cần sử dụng interface CategoryRepository nên mình sẽ khai báo autowire nó vào class CategoryController như sau:
1 2 |
@Autowired private CategoryRepository categoryRepository; |
Để xây dựng API thêm mới category, mình sẽ thêm mới một method để expose một POST request “/add” với data trong phần body là đối tượng document Category:
1 2 3 4 |
@PostMapping("/add") public Mono<Category> createCategory(@RequestBody Category category) { } |
Bởi vì Spring MongoDB Reactive đã hỗ trợ cho chúng ta phương thức save() để lưu data vào MongoDB nên mình chỉ cần gọi để sử dụng phương thức này như sau:
1 2 3 4 |
@PostMapping("/add") public Mono<Category> createCategory(@RequestBody Category category) { return categoryRepository.save(category); } |
Đến đây thì chúng ta đã hoàn thành việc xây dựng API thêm mới category cho Core Category Service, hãy test thử xem sao nhé các bạn.
Request:
Response:
Phần cuối cùng mà chúng ta cần làm là thêm mới Unit Test cho code mà chúng ta vừa thêm vào.
Phần đối tượng document Category với phần CategoryRepository thì không có gì phải test phải không các bạn? 🙂 Cái chúng ta cần test là phần CategoryController.
Mình sẽ tạo mới một class tên là CategoryControllerTest nằm trong package src/test/java để test cho class CategoryController.
Nguyên tắc của Unit Test là chúng ta sẽ mock tất cả những đoạn code gọi ra bên ngoài phương thức chúng ta cần test. Hiện tại trong class CategoryController của chúng ta chỉ có một phương thức, và trong phương thức này, nó có đang gọi tới phương thức save() của class CategoryRepository nên mình sẽ mock class CategoryRepository này.
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.huongdanjava.categoryservice; import org.mockito.Mock; import com.huongdanjava.categoryservice.repository.CategoryRepository; public class CategoryControllerTest { @Mock private CategoryRepository categoryRepository; } |
Tham khảo thêm về annotation @Mock của Mockito ở đây nhé các bạn.
Tiếp theo, mình sẽ sử dụng annotation @InjectMocks để sử dụng mock object của CategoryRepository trong CategoryController như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.categoryservice; import org.mockito.InjectMocks; import org.mockito.Mock; import com.huongdanjava.categoryservice.repository.CategoryRepository; public class CategoryControllerTest { @Mock private CategoryRepository categoryRepository; @InjectMocks private CategoryController categoryController; } |
Để sử dụng các đối tượng mock, chúng ta phải khởi tạo chúng trước mỗi lần test một phương thức nên mình sẽ thêm một phương thức với annotation @Before của JUnit như sau:
1 2 3 4 |
@Before public void init() { MockitoAnnotations.initMocks(this); } |
Code để test phương thức createCategory() như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@Test public void testCreateCategory() { // Create document need to insert into MongoDB Category category = new Category(); category.setCode("ABC"); category.setName("ZXC"); // The result should be the same with additional id field Category result = category; result.setId("123"); // Mock save() method of CategoryRepository when(categoryRepository.save(category)).thenReturn(Mono.just(result)); // Call method Mono<Category> createdCategoryAsMono = categoryController.createCategory(category); Category createdCategory = createdCategoryAsMono.block(); // Assertions assertEquals("ABC", createdCategory.getCode()); assertEquals("ZXC", createdCategory.getName()); assertEquals("123", createdCategory.getId()); } |
Chạy “Maven test” trong STS, các bạn sẽ không lỗi nào cả.
Davitluit
Xin cho hỏi, vì sao ở đây bạn không dùng những Collection như List mà phải dùng 2 đối tượng Mono và Flux?
Khanh Nguyen
Mình đang xây dựng ứng dụng này với Reactive support mà bạn! 🙂
Davitluit
Cái đó “để tránh cái việc mà cùng một lúc quá nhiều công việc ập tới, trong 1 thời điểm chúng ta chỉ xử lý một số công việc nào đó thôi, khi nào xử lý xong những công việc đó thì mới nhận tiếp những công việc mới” phải không bạn?
Khanh Nguyen
Yes bạn