我热衷于干净、隔离良好的单元测试。但我在这里偶然发现了“干净”的部分,用于测试使用DomainClassConverter功能来获取实体作为其映射方法的参数的 Controller 。
@Entity
class MyEntity {
@Id
private Integer id;
// rest of properties goes here.
}
Controller 是这样定义的
@RequestMapping("/api/v1/myentities")
class MyEntitiesController {
@Autowired
private DoSomethingService aService;
@PostMapping("/{id}")
public ResponseEntity<MyEntity> update(@PathVariable("id")Optional<MyEntity> myEntity) {
// do what is needed here
}
}
所以来自 DomainClassConverter
小documentation我知道它使用 CrudRepository#findById 来查找实体。我想知道的是如何在测试中干净地模拟它。
我通过执行以下步骤取得了一些成功:
- 创建一个我可以模拟的自定义转换器/格式化程序
- 使用上面的转换器实例化我自己的 MockMvc
- 在每次测试时重置模拟并更改行为。
问题是设置代码很复杂,因此很难调试和解释(我的团队 99% 都是来自 Rails 或 Uni 的初级人员,所以我们必须保持简单)。我想知道是否有一种方法可以从单元测试中注入(inject)所需的 MyEntity
实例,同时使用 @Autowired
MockMvc
继续测试。
目前,我正在尝试查看是否可以为 MyEntity
注入(inject) CrudRepository
的模拟,但没有成功。我已经有几年没有使用 Spring/Java 了(4),所以我对可用工具的了解可能不是最新的。
最佳答案
So from the DomainClassConverter small documentation I know that it uses CrudRepository#findById to find entities. What I would like to know is how can I mock that cleanly in a test.
您需要模拟在 CrudRepository#findById
之前调用的 2 个方法,以便返回您想要的实体。下面的示例使用 RestAssuredMockMvc
,但如果您也注入(inject) WebApplicationContext
,则可以使用 MockMvc 执行相同的操作。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SomeApplication.class)
public class SomeControllerTest {
@Autowired
private WebApplicationContext context;
@MockBean(name = "mvcConversionService")
private WebConversionService webConversionService;
@Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
SomeEntity someEntity = new SomeEntity();
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
}
}
在某个时刻,Spring Boot 将执行 WebConversionService::convert
,稍后将调用 DomainClassConverter::convert
,然后调用 invoker.invokeFindById
code>,它将使用实体存储库来查找实体。
那么为什么要模拟 WebConversionService
而不是 DomainClassConverter
呢?因为 DomainClassConverter
是在应用程序启动期间实例化的,无需注入(inject):
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<>(conversionService);
同时,WebConversionService
是一个 bean,允许我们模拟它:
@Bean
@Override
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
将模拟 bean 命名为 mvcConversionService
很重要,否则它不会替换原始 bean。
关于 stub ,您需要模拟 2 个方法。首先,您必须告诉您的模拟可以转换任何内容:
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
然后是 main 方法,它将匹配 URL 路径中定义的所需实体 ID:
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
到目前为止一切顺利。但也匹配目的地类型不是更好吗?类似于 eq(TypeDescriptor.valueOf(SomeEntity.class)) 的东西?可以,但是这会创建一个 TypeDescriptor 的新实例,当在域转换期间调用此 stub 时,该实例将不匹配。
这是我投入使用的最干净的解决方案,但我知道如果 Spring 允许的话,它可能会好得多。
关于java - 如何干净地测试使用 DomainClassConverter 检索参数的 Spring Controller ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56763939/