如何干净地测试使用DomainClassConverter检索参数的Spring控制器? [英] How to cleanly test Spring Controllers that retrieve parameters with DomainClassConverter?

查看:168
本文介绍了如何干净地测试使用DomainClassConverter检索参数的Spring控制器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我擅长进行干净,隔离良好的单元测试.但是我在这里的干净"部分碰巧要测试一个使用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:

  1. 创建一个我可以模拟的自定义转换器/格式化程序
  2. 使用上述转换器实例化我自己的MockMvc
  3. 重置模拟并在每次测试时更改行为.

问题在于设置代码很复杂,因此难以调试和解释(我的团队是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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆