如何干净地测试使用DomainClassConverter检索参数的Spring控制器? [英] How to cleanly test Spring Controllers that retrieve parameters with DomainClassConverter?
问题描述
我擅长进行干净,隔离良好的单元测试.但是我在这里的干净"部分碰巧要测试一个使用DomainClassConverter
功能来获取实体作为其映射方法参数的控制器.
I am big on clean well-isolated unit tests. But I am stumbling on the "clean" part here for testings a controller that uses DomainClassConverter
feature to get entities as parameters for its mapped methods.
@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
查找实体.我想知道的是如何在测试中清晰地进行模拟.
通过执行以下步骤,我取得了一些成功:
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.
I have had some success by doing this steps:
- 创建一个我可以模拟的自定义转换器/格式化程序
- 使用上述转换器实例化我自己的MockMvc
- 重置模拟并在每次测试时更改行为.
问题在于设置代码很复杂,因此难以调试和解释(我的团队是99%的初级人员,来自Rails或uni,所以我们必须保持简单).我想知道是否有办法从单元测试中注入所需的MyEntity
实例,同时继续使用@Autowired
MockMvc
进行测试.
The problem is that the setup code is complex and thus hard to debug and explain (my team is 99% junior guys coming from rails or uni so we have to keep things simple). I was wondering if there is a way to inject the desired MyEntity
instances from my unit test while keep on testing using the @Autowired
MockMvc
.
当前,我正在尝试查看是否可以为MyEntity
注入CrudRepository
的模拟,但是没有成功.几年(4)以来,我还没有从事过Spring/Java的工作,所以我对可用工具的了解可能不是最新的.
Currently I am trying to see if I can inject a mock of the CrudRepository
for MyEntity
but no success. I have not worked in Spring/Java in a few years (4) so my knowledge of the tools available might not be up to date.
推荐答案
因此从DomainClassConverter小型文档中,我知道它使用CrudRepository#findById查找实体.我想知道的是如何在测试中进行简洁的模拟.
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.
您将需要模拟2个在CrudRepository#findById
之前调用的方法,以便返回所需的实体.下面的示例使用的是RestAssuredMockMvc
,但是如果同时注入WebApplicationContext
,则可以对MockMvc执行相同的操作.
You will need to mock 2 methods that are called prior the CrudRepository#findById
in order to return the entity you want. The example below is using RestAssuredMockMvc
, but you can do the same thing with MockMvc if you inject the WebApplicationContext
as well.
@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
的操作,该操作将使用实体存储库来查找实体.
At some point Spring Boot will execute the WebConversionService::convert
, which will later call DomainClassConverter::convert
and then something like invoker.invokeFindById
, which will use the entity repository to find the entity.
那为什么要模拟WebConversionService
而不是DomainClassConverter
?因为DomainClassConverter
是在应用程序启动期间实例化的,所以没有注入:
So why mock WebConversionService
instead of DomainClassConverter
? Because DomainClassConverter
is instantiated during application startup without injection:
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<>(conversionService);
同时,WebConversionService
是一个可以让我们模拟它的bean:
Meanwhile, WebConversionService
is a bean which will allow us to mock it:
@Bean
@Override
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
将模拟bean命名为mvcConversionService
很重要,否则它将不会替换原始bean.
It is important to name the mock bean as mvcConversionService
, otherwise it won't replace the original bean.
关于存根,您将需要模拟2种方法.首先,您必须告诉您的模拟对象可以转换任何内容:
Regarding the stubs, you will need to mock 2 methods. First you must tell that your mock can convert anything:
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
然后是main方法,它将与URL路径中定义的所需实体ID相匹配:
And then the main method, which will match the desired entity ID defined in the URL path:
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
到目前为止,一切都很好.但是匹配目标类型也不会更好吗? eq(TypeDescriptor.valueOf(SomeEntity.class))
之类的东西?可以,但是这会创建TypeDescriptor的新实例,当在域转换过程中调用此存根时,该实例将不匹配.
So far so good. But wouldn't be better to match the destination type as well? Something like eq(TypeDescriptor.valueOf(SomeEntity.class))
? It would, but this creates a new instance of a TypeDescriptor, which will not match when this stub is called during the domain conversion.
这是我使用过的最干净的解决方案,但是我知道,如果Spring允许的话,可能会更好.
This was the cleanest solution I've put to work, but I know that it could be a lot better if Spring would allow it.
这篇关于如何干净地测试使用DomainClassConverter检索参数的Spring控制器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!