13.2.3 测试响应式 MongoDB Repository
为 MongoDB Repository 编写测试的关键是使用 @DataMongoTest
。此注解执行与 @DataR2dbcTest
类似的功能,这是我们在本章前面使用过的。它确保 Spring 应用程序上下文生成 Repository 相关 bean,并且可注入测试类。从那里,测试类可以使用这些注入的Repository 来设置测试数据,并对其执行其他操作。
例如,考虑清单 13.15 中的 IngredientRepositoryTest 类测试 IngredientRepository,断言 Ingredient 对象可以写入数据库。
清单 13.15 测试响应式 Mongo Repository。
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));
});
}
}
此测试与我们前面编写的基于 R2DBC 的 Repository 测试类略有不同。它首先将三个 Ingredient 对象写入数据库。然后使用两个 StepVerifier 验证 Ingredient 是否可以通过 Repository 读到,先获取所有 Ingredient 对象的集合,然后通过 ID 获取单个 Ingredient。
此外,与前面基于 R2DBC 的测试一样,@DataMongoTest
注解将查找添加了 @SpringBootConfiguration
注解的类,以创建应用程序上下文。前面创建的那个,这里也可以使用。
这里的独特之处在于,第一步 StepVerifier 将所有 Ingredient 对象收集到一个 ArrayList,然后断言 ArrayList 包含每个 Ingredient。findAll()
方法不能保证结果文档的顺序,这使得 assertNext()
或 expectNext()
容易失败。通过收集所有产生的Ingredient,将对象放入列表中,我们可以断言该列表包含所有三个对象,而不管它们的顺序如何。
OrderRepository 的测试看起来很类似,如清单 13.16 所示。
清单 13.16 测试 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;
}
}
shouldSaveAndFetchOrders()
方法所做的第一件事是构造一个订单,有完整的客户和付款信息和一些玉米卷。(为了简洁起见,createOrder()
方法的详细信息被省略。)然后它使用一个 StepVerifier 保存 TacoOrder 对象,并断言 save()
方法返回保存的 TacoOrder。然后尝试通过其 ID 获取订单,并断言它收到了完整的 TacoOrder。最后,它获取所有 TacoOrder 对象(应该只有一个),并断言它是预期的 TacoOrder 对象。
如前所述,您将需要一个 MongoDB 服务器,并侦听端口 27017,以运行此测试。而 Flapdoodle 这个嵌入式 MongoDB 不能很好地与响应式 Repository 配合使用。如果您的计算机上安装了 Docker,则可以轻松启动 MongoDB 服务器,并侦听端口 27017,如下所示:
$ docker run -p27017:27017 mongo
还有其他方法可以获得 MongoDB 服务器。更多细节请参阅以下网址的文档:https://www.mongodb.com/ 。
现在,我们已经了解了如何为 R2BDC 和 MongoDB 创建响应式 Repository,下面让我们看一看另一个 Spring Data 选择:Cassandra。