我擅长进行干净,隔离良好的单元测试。但是我在这里的“干净”部分碰巧测试一个控制器,该控制器使用DomainClassConverter功能获取实体作为其映射方法的参数。
DomainClassConverter
@Entity class MyEntity { @Id private Integer id; // rest of properties goes here. }
像这样定义控制器
@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小型文档中,我知道它用于CrudRepository#findById查找实体。我想知道的是如何在测试中清晰地进行模拟。通过执行以下步骤,我取得了一些成功:
CrudRepository#findById
问题在于设置代码很复杂,因此难以调试和解释(我的团队是来自Rails或uni的99%的初级人员,因此我们必须保持简单)。我想知道是否有一种方法可以MyEntity在继续使用进行测试的同时从单元测试中注入所需的实例@Autowired MockMvc。
MyEntity
@Autowired
MockMvc
目前,我正在尝试查看是否可以插入CrudRepositoryfor 的模拟物,MyEntity但没有成功。几年来我一直没有在Spring / Java中工作(4),所以我对可用工具的了解可能不是最新的。
CrudRepository
因此,从DomainClassConverter小型文档中,我知道它使用CrudRepository#findById查找实体。我想知道的是如何在测试中清晰地进行模拟。
您将需要模拟2个在之前调用的方法,CrudRepository#findById以便返回所需的实体。下面的示例使用RestAssuredMockMvc,但是如果同时注入MockMvc,则可以执行相同的操作WebApplicationContext。
RestAssuredMockMvc
WebApplicationContext
@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,该操作将使用实体存储库查找实体。
WebConversionService::convert
DomainClassConverter::convert
invoker.invokeFindById
那么为什么要嘲笑WebConversionService而不是DomainClassConverter呢?因为DomainClassConverter是在应用程序启动期间实例化的,没有注入:
WebConversionService
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。
mvcConversionService
关于存根,您将需要模拟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的新实例,当在域转换期间调用此存根时,它将不匹配。
eq(TypeDescriptor.valueOf(SomeEntity.class))
这是我使用过的最干净的解决方案,但是我知道,如果Spring允许,它可能会更好。