如何模拟 Spring ConversionService?
How to mock Spring ConversionService?
我编写的 Web 应用程序按预期运行。现在我想对控制器方法进行单元测试。这些方法的模式是:
- 将 http 请求对象 (DTO) 转换为域对象
- 在服务层使用域对象调用业务逻辑
- 将业务逻辑的响应转换为响应 (DTO) 对象
对于转换步骤,我使用 Spring ConversionService,一个配置如下的 bean:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="my.package.RequestToDomainConverter" />
<bean class="my.package.DomainToResponseConverter" />
</list>
</property>
这个 bean 自动连接到我的控制器中:
@Autowired
ConversionService conversionService;
并像这样使用:
@RequestMapping(method = RequestMethod.POST,
headers = "Accept=application/json")
@ResponseStatus(value = HttpStatus.CREATED)
@ResponseBody
public ResponseDTO createSelection(
@RequestBody RequestDTO requestDTO,
HttpServletResponse response,
Authentication authentication ) {
DomainObject domainObject = conversionService.convert(requestDTO, DomainObject.class);
// note: during test the conversionService returns null here...
DomainObject businessAnswer = BusinessLayer.doService(domainObject);
ResponseDTO responseDTO = conversionService.convert(businessAnswer, ResponseDTO.class);
return responseDTO;
}
如上所述,当部署到应用程序服务器时,应用程序按预期工作。
我的测试class构建如下:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations={"classpath:/my/package/MyControllerTest-context.xml"})
public class MyControllerTest {
private MockMvc mockMvc;
@Mock
private ConversionService conversionService;
@Mock
private BusinessLayer businessLayer;
@InjectMocks
private MyController myController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetum(myController).build();
}
@Test
public void testCreateSelection(){
// create a json string representation of the requestDTO
String jsonDTO = new String("a valid json representation");
// create objects to convert
RequestDTO myRequestDTO = new RequestDTO();
DomainObject myDomainObject = new DomainObject();
ResponseDTO responseDTO = new ResponseDTO();
// instruct the conversionservice mock to return the expected objects
when(conversionService.convert(myRequestDTO, DomainObject.class))
.thenReturn(myDomainObject);
when(conversionService.convert(domainResponse, ResponseDTO.class))
.thenReturn(myResponseDTO);
// the businessLayer mock returns the same object that was given to it
when(businessLayer.doService(domainObject))
.thenReturn(domainObject);
//create the necessary http headers
HttHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Accept", "application/json");
//execute the controller method
mockMvc.perform(post("/selection")
.content(jsonDTO)
.contentType(MediaType.APPLICATION_JSON)
.headers(httpHeaders))
.andExpect(status().isOk());
// further testing...
}
}
当 运行 在调试模式下进行此测试时,我看到我的控制器中的 createSelection 方法被成功调用,而在该方法中,requestDTO 对象具有提供给 jsonDTO 对象的值。
但是,当要求将 requestDTO 转换为 DomainObject 时,conversionService return为空。
为什么会这样,如何设置我的测试以使其 return 转换后的对象?
是因为下面一行:
when(conversionService.convert(myRequestDTO, DomainObject.class))
.thenReturn(myDomainObject);
方法期望接收相同的对象 myRequestDTO
,这与您的情况不同,因为在您的控制器内部您将创建相同 class 的另一个实例。两者都是从相同的 class 创建的,但具有不同的身份。相反,您可以使用:
when(conversionService.convert(any(DomainObject.class), Matchers.<DomainObject>any()))
.thenReturn(myDomainObject);
这允许期待同一个对象,这个对象的身份无关紧要
我编写的 Web 应用程序按预期运行。现在我想对控制器方法进行单元测试。这些方法的模式是:
- 将 http 请求对象 (DTO) 转换为域对象
- 在服务层使用域对象调用业务逻辑
- 将业务逻辑的响应转换为响应 (DTO) 对象
对于转换步骤,我使用 Spring ConversionService,一个配置如下的 bean:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="my.package.RequestToDomainConverter" />
<bean class="my.package.DomainToResponseConverter" />
</list>
</property>
这个 bean 自动连接到我的控制器中:
@Autowired
ConversionService conversionService;
并像这样使用:
@RequestMapping(method = RequestMethod.POST,
headers = "Accept=application/json")
@ResponseStatus(value = HttpStatus.CREATED)
@ResponseBody
public ResponseDTO createSelection(
@RequestBody RequestDTO requestDTO,
HttpServletResponse response,
Authentication authentication ) {
DomainObject domainObject = conversionService.convert(requestDTO, DomainObject.class);
// note: during test the conversionService returns null here...
DomainObject businessAnswer = BusinessLayer.doService(domainObject);
ResponseDTO responseDTO = conversionService.convert(businessAnswer, ResponseDTO.class);
return responseDTO;
}
如上所述,当部署到应用程序服务器时,应用程序按预期工作。
我的测试class构建如下:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations={"classpath:/my/package/MyControllerTest-context.xml"})
public class MyControllerTest {
private MockMvc mockMvc;
@Mock
private ConversionService conversionService;
@Mock
private BusinessLayer businessLayer;
@InjectMocks
private MyController myController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetum(myController).build();
}
@Test
public void testCreateSelection(){
// create a json string representation of the requestDTO
String jsonDTO = new String("a valid json representation");
// create objects to convert
RequestDTO myRequestDTO = new RequestDTO();
DomainObject myDomainObject = new DomainObject();
ResponseDTO responseDTO = new ResponseDTO();
// instruct the conversionservice mock to return the expected objects
when(conversionService.convert(myRequestDTO, DomainObject.class))
.thenReturn(myDomainObject);
when(conversionService.convert(domainResponse, ResponseDTO.class))
.thenReturn(myResponseDTO);
// the businessLayer mock returns the same object that was given to it
when(businessLayer.doService(domainObject))
.thenReturn(domainObject);
//create the necessary http headers
HttHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Accept", "application/json");
//execute the controller method
mockMvc.perform(post("/selection")
.content(jsonDTO)
.contentType(MediaType.APPLICATION_JSON)
.headers(httpHeaders))
.andExpect(status().isOk());
// further testing...
}
}
当 运行 在调试模式下进行此测试时,我看到我的控制器中的 createSelection 方法被成功调用,而在该方法中,requestDTO 对象具有提供给 jsonDTO 对象的值。
但是,当要求将 requestDTO 转换为 DomainObject 时,conversionService return为空。
为什么会这样,如何设置我的测试以使其 return 转换后的对象?
是因为下面一行:
when(conversionService.convert(myRequestDTO, DomainObject.class))
.thenReturn(myDomainObject);
方法期望接收相同的对象 myRequestDTO
,这与您的情况不同,因为在您的控制器内部您将创建相同 class 的另一个实例。两者都是从相同的 class 创建的,但具有不同的身份。相反,您可以使用:
when(conversionService.convert(any(DomainObject.class), Matchers.<DomainObject>any()))
.thenReturn(myDomainObject);
这允许期待同一个对象,这个对象的身份无关紧要