13.2.3 Kiểm thử các repository MongoDB phản ứng
Chìa khóa để viết kiểm thử cho các repository MongoDB là đánh dấu lớp kiểm thử với @DataMongoTest. Annotation này hoạt động tương tự như annotation @DataR2dbcTest mà chúng ta đã sử dụng ở phần trước của chương này. Nó đảm bảo rằng một ngữ cảnh ứng dụng Spring được tạo ra với các repository đã được sinh ra sẵn sàng dưới dạng các bean để có thể tiêm vào trong bài kiểm thử. Từ đó, bài kiểm thử có thể sử dụng các repository đã được tiêm để thiết lập dữ liệu kiểm thử và thực hiện các thao tác khác với cơ sở dữ liệu.
Ví dụ, hãy xem xét lớp IngredientRepositoryTest trong đoạn liệt kê dưới đây, kiểm thử IngredientRepository, đảm bảo rằng các đối tượng Ingredient có thể được ghi vào và đọc từ cơ sở dữ liệu.
Liệt kê 13.15 Kiểm thử một Mongo repository phản ứng
package tacos.data;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import tacos.Ingredient;
import tacos.Ingredient.Type;
@DataMongoTest
public class IngredientRepositoryTest {
@Autowired
IngredientRepository ingredientRepo;
@BeforeEach
public void setup() {
Flux<Ingredient> deleteAndInsert = ingredientRepo.deleteAll()
.thenMany(ingredientRepo.saveAll(
Flux.just(
new Ingredient("FLTO", "Flour Tortilla", Type.WRAP),
new Ingredient("GRBF", "Ground Beef", Type.PROTEIN),
new Ingredient("CHED", "Cheddar Cheese", Type.CHEESE)
)));
StepVerifier.create(deleteAndInsert)
.expectNextCount(3)
.verifyComplete();
}
@Test
public void shouldSaveAndFetchIngredients() {
StepVerifier.create(ingredientRepo.findAll())
.recordWith(ArrayList::new)
.thenConsumeWhile(x -> true)
.consumeRecordedWith(ingredients -> {
assertThat(ingredients).hasSize(3);
assertThat(ingredients).contains(
new Ingredient("FLTO", "Flour Tortilla", Type.WRAP));
assertThat(ingredients).contains(
new Ingredient("GRBF", "Ground Beef", Type.PROTEIN));
assertThat(ingredients).contains(
new Ingredient("CHED", "Cheddar Cheese", Type.CHEESE));
})
.verifyComplete();
StepVerifier.create(ingredientRepo.findById("FLTO"))
.assertNext(ingredient -> {
ingredient.equals(new Ingredient("FLTO", "Flour Tortilla", Type.WRAP));
});
}
}Bài kiểm thử này tương tự nhưng vẫn hơi khác so với bài kiểm thử repository dựa trên R2DBC mà chúng ta đã viết trước đó trong chương này. Nó bắt đầu bằng cách ghi ba đối tượng Ingredient vào cơ sở dữ liệu. Sau đó, nó sử dụng hai đối tượng StepVerifier để xác minh rằng các đối tượng Ingredient có thể được đọc thông qua repository, đầu tiên là dưới dạng tập hợp tất cả các đối tượng Ingredient, và sau đó là truy xuất một Ingredient duy nhất theo ID của nó.
Tương tự như bài kiểm thử R2DBC trước đó, annotation @DataMongoTest cũng sẽ tìm kiếm một lớp được đánh dấu @SpringBootConfiguration để tạo ngữ cảnh ứng dụng. Một bài kiểm thử giống như cái đã tạo trước đó cũng sẽ hoạt động tốt ở đây.
Điểm đặc biệt ở đây là StepVerifier đầu tiên sẽ thu thập tất cả các đối tượng Ingredient vào một ArrayList và sau đó xác nhận rằng ArrayList này chứa từng đối tượng Ingredient. Phương thức findAll() không đảm bảo thứ tự ổn định của các tài liệu kết quả, điều này khiến việc sử dụng assertNext() hoặc expectNext() dễ bị thất bại. Bằng cách thu thập tất cả các đối tượng Ingredient kết quả vào một danh sách, chúng ta có thể xác nhận rằng danh sách có chứa cả ba đối tượng, bất kể thứ tự của chúng.
Một bài kiểm thử cho OrderRepository cũng tương tự, như minh họa dưới đây.
Liệt kê 13.16 Kiểm thử Mongo OrderRepository
package tacos.data;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import reactor.test.StepVerifier;
import tacos.Ingredient;
import tacos.Taco;
import tacos.TacoOrder;
import tacos.Ingredient.Type;
@DataMongoTest
public class OrderRepositoryTest {
@Autowired
OrderRepository orderRepo;
@BeforeEach
public void setup() {
orderRepo.deleteAll().subscribe();
}
@Test
public void shouldSaveAndFetchOrders() {
TacoOrder order = createOrder();
StepVerifier
.create(orderRepo.save(order))
.expectNext(order)
.verifyComplete();
StepVerifier
.create(orderRepo.findById(order.getId()))
.expectNext(order)
.verifyComplete();
StepVerifier
.create(orderRepo.findAll())
.expectNext(order)
.verifyComplete();
}
private TacoOrder createOrder() {
TacoOrder order = new TacoOrder();
...
return order;
}
}Điều đầu tiên mà phương thức shouldSaveAndFetchOrders() làm là tạo một đơn hàng, bao gồm thông tin khách hàng và thanh toán, cùng với một vài taco. (Để ngắn gọn, tôi đã lược bỏ chi tiết của phương thức createOrder().) Sau đó, nó sử dụng StepVerifier để lưu đối tượng TacoOrder và xác nhận rằng phương thức save() trả về đối tượng TacoOrder đã lưu. Tiếp theo, nó cố gắng truy xuất đơn hàng theo ID của nó và xác nhận rằng đơn hàng đầy đủ được nhận lại. Cuối cùng, nó truy xuất tất cả các đối tượng TacoOrder — chỉ nên có một — và xác nhận rằng đó là TacoOrder mong đợi.
Như đã đề cập ở trên, bạn sẽ cần một server MongoDB có sẵn và đang lắng nghe trên cổng 27017 để chạy bài kiểm thử này. MongoDB nhúng Flapdoodle không hoạt động tốt với các repository phản ứng. Nếu bạn đã cài Docker trên máy, bạn có thể dễ dàng khởi động một server MongoDB mở ra cổng 27017 như sau:
docker run -p27017:27017 mongoCác cách khác để thiết lập MongoDB cũng có thể thực hiện được. Tham khảo tài liệu tại https://www.mongodb.com/ để biết thêm chi tiết.
Giờ đây khi chúng ta đã thấy cách tạo repository phản ứng cho R2DBC và MongoDB, hãy cùng xem một tùy chọn nữa trong Spring Data cho lưu trữ phản ứng: Cassandra.
