13.1.3 测试 R2DBC Repository
Spring Data R2DBC 支持为 R2DBC Repository 编写集成测试类。具体来说,@DataR2dbcTest 注解放在测试类上时,会导致 Spring 使用生成的 Spring Data R2DBC Repository 作为 bean 来创建应用程序上下文,并注入到测试类中。就像我们在上一章中使用的 StepVerifier,这使我们能够针对我们创建的所有 Repository 编写自动化测试类。
为了简洁起见,我们将只关注一个测试类:IngredientRepositoryTest。这将测试 IngredientRepository,验证它是否可以保存 Ingredient 对象、获取单一 Ingredient,并获取所有保存的 Ingredient 对象。清单 13.7 显示了这个测试类。
清单 13.7 测试 Spring Data R2DBC 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.r2dbc.DataR2dbcTest;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import tacos.Ingredient;
import tacos.Ingredient.Type;
@DataR2dbcTest
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.findBySlug("FLTO"))
.assertNext(ingredient -> {
ingredient.equals(new Ingredient("FLTO", "Flour Tortilla", Type.WRAP));
});
}
}
shouldSaveAndFetchIngredients()
方法首先创建 Flux<Ingredient>
对象。从这个 Flux 中,它使用 flatMap()
操作通过注入的 IngredientRepository 上的 save()
方法保存每一个 Ingredient。调用 subscribe()
将打开通过 Flux 的数据流,导致 Ingredient 对象被保存。
接下来,从 Repository 的 findBySlug()
方法返回的 Mono<Ingredient>
,创建出了一个 StepVerifier,这。Mono<Ingredient>
中应该有一个 Ingredient 对象。也是 assertNext()
方法验证的内容,这是一个 slug 值为 “FLTO”的 Ingredient 对象。最后,验证了 Mono 的完整性。
最后,根据 Repository 的 findAll()
方法返回的 Flux<Ingredient>
,创建出了另一个 StepVerifier。一次一个,它断言,从该 Flux 流出的 Ingredient 与最初测试开始时保存的三个 Ingredient 对象相匹配。与其他 StepVerifier 一样,对 verifyComplete()
的调用验证 Mono 是完整的,没有更多的 Ingredient 对象流出了。
虽然我们只专注于测试 IngredientRepository,但同样的技术也可以应用于测试任何 Spring Date R2BDC 的 Repository。
到目前为止,一切顺利。我们现在已经定义了领域类型及其各自的 Repository。我们已经编写了一个测试类来验证它们是否有效。如果您愿意,我们可以就这样使用。但是这些 Repository 使 TacoOrder 的持久性变得不方便,因为您必须首先创建和持久化属于该 TacoOrder 的 Taco 对象,然后持久化属于该 TacoOrder 对象。当您查看 TacoOrder 时,您只会得到 Taco ID 而不是完整的 Taco 对象集合。
如果我们能够将 TacoOrder 作为聚合根,与拥有的所有 Taco 对象一起持久化,那就太好了。那样,如果我们找到一个 TacoOrder,就能得到完整的 Taco对象(而不仅仅是 ID)。让我们定义一个 Service 级别类,该类位于 OrderRepository 和 TacoRepository 之上,以模拟第 3 章的 OrderRepository 的行为。