java - 如何干净地测试使用 DomainClassConverter 检索参数的 Spring Controller ?

标签 java spring unit-testing spring-mvc mockito

我热衷于干净、隔离良好的单元测试。但我在这里偶然发现了“干净”的部分,用于测试使用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
    }
}

所以来自 DomainClassConverterdocumentation我知道它使用 CrudRepository#findById 来查找实体。我想知道的是如何在测试中干净地模拟它。 我通过执行以下步骤取得了一些成功:

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

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

相关文章:

java - 将 iCalendar 发送到网页时没有行分隔。 java

java - 有没有支持快速插入和中值计算的数据结构?

java - 《Java 编程语言》第四版练习 3.3

json - CXF 3.0 和 Spring - 未找到响应类的消息正文阅读器

java - 模式观察者和Spring

javascript - Angular 如何知道 $httpBackend 正在替换 Angularjs 测试中的 $http ?

java.util.UnknownFormatConversionException :

java - swing应用程序、spring应用程序上下文错误

java - groovy soapUI反序列化

javascript - 在 Mocha 中所有测试用例之前的钩子(Hook)之前