我有一个使用Spring MVC的项目。 我正在尝试为服务模块编写单元测试, 这是在项目的架构中。 所有服务类都从名为"BaseService"的超类扩展而来。 BaseService是这样的:
public abstract class BaseService {
private static final Logger logger = LoggerFactory.getLogger(BaseService.class);
@Autowired(required = true)
private HttpServletRequest request;
@Autowired
private ReloadableResourceBundleMessageSource messageSource;
/*
* Injecting Mapper
*/
@Resource
private Mapper mapper;
...
public <T extends BaseBVO, S extends BaseVO> T voToBvo (S vo, Class<? extends BaseBVO> bvoClass) {
if (vo != null)
{
return (T) mapper.map(vo , bvoClass);
}
else
{
return null;
}
}
现在我在服务模块中有一个使用该方法的方法:
"voToBvo (S vo, Class<? extends BaseBVO> bvoClass")
喜欢这个:
public List<AdcOptionBVO> findOptionByAyantDroitIdAndProduitId (Long idAyantDroit, String idProduit) {
....
list = listVoToBvo(adcList , AdcOptionBVO.class);
logger.info("Returning List of ayrp count= {}" , list.size());
return list;
}
我的测试是这样的:
@RunWith(MockitoJUnitRunner.class)
public class AdcServiceImplTest{
@Mock
private Mapper mapper;
@Test
public void shouldSuccessFindOptionByAyantDroitIdandProduitId() {
//Given
List<AdcVO> adcVOList = new ArrayList<>();
adcVOList.add(new AdcVO());
List<AdcOptionBVO> listAdcOptionBVO = new ArrayList<>();
listAdcOptionBVO.add(new AdcOptionBVO());
List<BaseBVO> baseBVOs = new ArrayList<>();
//When
when(repository
.finAdcByOptionOrderByRUAndPrio(anyLong(), anyString())).thenReturn(adcVOList);
when(baseService.listVoToBvo(adcVOList, AdcOptionBVO.class)).thenReturn(baseBVOs);
//Then
assertEquals(adcService
.findOptionByAyantDroitIdAndProduitId(anyLong(), anyString()).size(), adcVOList.size());
}
}
我在调用映射器时.java在 BaseService 中得到一个 java.lang.NullPointerException。
@Resource
private Mapper mapper;
映射器为空!
我想模拟该方法:
listVoToBvo(adcList , AdcOptionBVO.class);
请帮忙。
解决方案是使用注入参数为abstarct类添加构造函数,在我的例子中(映射器),然后在服务调用super(mapper);
在服务的构造函数中 所以在测试中我模拟映射器映射器,然后我在@Before
中实例化我的类,所以映射器作为模拟传递给 abstarct 类而不是空。它对我来说效果很好。我希望它的克莱尔
我建议你,首先在你的BaseService文件中进行更改。 更改@Autowired的编写方式,如下所示
private HttpServletRequest request;
@Autowired(required = true)
public void setSpellChecker( HttpServletRequest request ){
this.request = request;
}
@Autowired
private ReloadableResourceBundleMessageSource messageSource;
public void setSpellChecker( ReloadableResourceBundleMessageSource messageSource ){
this.messageSource = messageSource;
}
当你这样做时,现在你可以很容易地实现具有依赖性的测试用例。
由于当前设置了测试,因此不会注入基类中的任何注入项。 您可以使用两种技术来解决此问题:
- 使用 Spring 测试运行器(名为 SpringJUnit4ClassRunner.class)运行。 这对于超出范围的集成测试很好。
- 用手进行注射。 使用反射并将 BaseService 类中的映射器设置为模拟对象。
使用技术 2.
创建用于测试的实用程序,该实用程序使用反射设置数据成员。 在谷歌上搜索"设置父类变量反射Java"或类似的东西,你会发现示例反射代码.d 该实用工具应包装反射代码,并获取数据成员名称、类对象、对在其中设置值的对象的引用以及执行反射所需的任何其他内容的参数。
另一种选择是找到一个反射实用程序(google,apache和spring)都应该有一个。
下面是一个(不起作用的)示例:
public static void setFieldObject(
final String fieldName,
final Object fieldValue,
final Object targetObject,
final Class targetClass)
throws
NoSuchFieldException,
IllegalAccessException
{
final Field targetField;
targetField = targetClass.getDeclaredField(fieldName);
targetField.setAccessible(true);
targetField.set(
targetObject,
fieldValue);
}
注入模拟后,尝试使用@Before
初始化它们。
例如:
@Mock
private HttpServletRequest request;
@Mock
private Mapper mapper;
@InjectMocks
private BaseService baseService;
@Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
为我工作。
实际上,您的代码在某种程度上并不那么清晰,但是使用Mockito模拟自动连线字段并不那么复杂,您只需遵循正确的步骤即可。
假设我们要为BaseService
创建一个单元测试:
public class BaseServiceTest {
private static final BaseVO TEST_BaseVO = //intialize here...;
private static final BaseBVO TEST_BaseBVO = //intialize here...;
private static final BaseBVO TEST_BaseBVO_RESULT = //intialize here...;
@InjectMocks
private BaseService baseService;
@Mock
private HttpServletRequest request;
@Mock
private ReloadableResourceBundleMessageSource messageSource;
@Mock
private Mapper mapper;
@BeforeMethod
public void initMocks(){
//This line of code is so important!! to initialize annotated fields. without it these fields would be null.
//Or just annotate this class with @RunWith(MockitoJUnitRunner.class)
MockitoAnnotations.initMocks(this);
//Here all fields annotated with @Mock or @Spy should be initialize and injected into BaseService.
}
@Test
public void voToBvo_should_return_null_when_vo_is_null(){
//Call testing method
BaseBVO result = baseService.voToBvo(null, TEST_BaseBVO.class);
//Assertions
Assert.assertNull(result);
}
@Test
public void voToBvo_should_not_return_null_when_vo_is_not_null(){
//Mock calls
Mockito.doReturn(TEST_BaseBVO_RESULT).when(mapper).map(TEST_BaseVO, TEST_BaseBVO.class)
//Call testing method
BaseBVO result = baseService.voToBvo(TEST_BaseVO, TEST_BaseBVO.class);
//Assertions
Assert.assertNotNull(result);
Assert.assertEquals(TEST_BaseBVO_RESULT, result);
}
}
听起来这个单元测试可能很愚蠢(实际上它:p很愚蠢),但它描述了使用 Mockito 创建成功 UT 的基本步骤以及如何防止 NPE。