我非常喜欢干净且隔离良好的单元测试.但是我在这里的干净"部分绊倒了测试一个控制器,该控制器使用 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 小 documentation 我知道它使用 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:
问题在于设置代码很复杂,因此很难调试和解释(我的团队 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 的模拟,但没有成功.我已经有几年没有在 Spring/Java 中工作了 (4),所以我对可用工具的了解可能不是最新的.
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.
您需要模拟在 CrudRepository#findById 之前调用的 2 个方法,以便返回您想要的实体.下面的示例使用 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.invokeFindByIdcode>,它将使用实体存储库来查找实体.
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;
}
将mock 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 Controller?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持html5模板网!
如何检测 32 位 int 上的整数溢出?How can I detect integer overflow on 32 bits int?(如何检测 32 位 int 上的整数溢出?)
return 语句之前的局部变量,这有关系吗?Local variables before return statements, does it matter?(return 语句之前的局部变量,这有关系吗?)
如何将整数转换为整数?How to convert Integer to int?(如何将整数转换为整数?)
如何在给定范围内创建一个随机打乱数字的 intHow do I create an int array with randomly shuffled numbers in a given range(如何在给定范围内创建一个随机打乱数字的 int 数组)
java的行为不一致==Inconsistent behavior on java#39;s ==(java的行为不一致==)
为什么 Java 能够将 0xff000000 存储为 int?Why is Java able to store 0xff000000 as an int?(为什么 Java 能够将 0xff000000 存储为 int?)