本文主要阐述了如何基于Junit和Mockito针对微服务做单元测试。
参考资料: https://www.tianmaying.com/tutorial/JunitForSpringBoot
Mockito常用注解如下:
@InjectMock
一般在测试主类上使用
@Spy
如果需要调用测试主类本身的方法时,需要用到该注解做部分模拟,
一般结合@InjectMock一起使用
@Mock
需要被模拟的依赖类
when(对象名.方法名).thenReturn(返回值) //需要模拟依赖类的方法时使用
doReturn(返回值).when(测试主类).方法 //需要模拟测试主类自己的service方法时使用
具体使用步骤:
@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
@Mock
Service1 service1;
@InjectMock
@Spy
MainService mainService;
@Test
public void testGetData(){
when(service1.getOne()).thenReturn(返回值); //需要模拟依赖类的方法时使用
doReturn(返回值).when(mainService).getSubData(); //需要依赖测试主类自己的service方法时使用
mainService.getData();
//编写单元测试验证语句,执行单元测试
可以结合verify 和Assert验证语句对mainService中具体的方法进行验证
...
....
}
要点:
1.Mock出所有需要调用的远程服务和持久层方法,使得单元测试不依赖与其他服务和数据库独立运行。Mockito的用法可以参考: http://blog.csdn.net/anlegor/article/details/44452011
2.service中过于简单的业务,这种则选择不做测试 如:只是仅仅进行增删改查而没有其他逻辑的方法。
而对于复杂的业务,可能个出现多种情况的应该每种情况做出一个单元测试。
3.断言Assert:JUnit为我们提供了一些辅助函数,他们用来帮助我们确定被测试的方法是否按照预期的效果正常工作,通常,把这些辅助函数称为断言。
单元测试的最后我们都应加上断言,以确定我们的方法执行后的结果是我们所预期的。断言的使用不应当仅仅是判断是否为null,而是应该把测试后的结果集取出来,判断具体某个值是否和我们预期的匹配。
如果需要验证方法是否被mock且是否为所执行的参数调用,可以使用verify来验证。
4. 数据库层面的模拟建议借助于 H2 Db 内存数据库进行测试
示例:
业务代码
public class InventoryServiceImpl implements InventoryService {
@Autowired
private PartsStockRepository partsStockRepository;
@Autowired
private SparePartsServiceClient sparePartsServiceClient;
@Autowired
private PriceServiceClient priceServiceClient;
@Autowired
private WarehouseServiceClient warehouseServiceClient;
public List findPartStockVoByCheckAreaId(int checkAreaId) {
List partsStockEntities = partsStockRepository.findPartsStockByCheckAreaId(checkAreaId);
List partStockVos = new ArrayList<>();
for (PartsStockEntity partsStockEntity : partsStockEntities) {
PartStockVo partStockVo = TransferObject.transferObject(partsStockEntity, PartStockVo.class);
Map spareParts = sparePartsServiceClient.findSparePartById(partStockVo.getSparePartId());
partStockVo.setSparePartCode((String) spareParts.get("sparePartCode"));
partStockVo.setSparePartName((String) spareParts.get("sparePartName"));
Map warehouseAreas = warehouseServiceClient.findWarehouseAreaById(partStockVo.getCheckAreaLocationId());
partStockVo.setCheckAreaLocation((String) warehouseAreas.get("warehouseAreaCode"));
Map recommendLocation = warehouseServiceClient.findWarehouseAreaByIdAndAreaKind(partsStockEntity.getWarehouseId(), 2);
partStockVo.setRecommendedLocation(recommendLocation.get("warehouseAreaCode").toString());
partStockVo.setRecommendedLocationId(Integer.parseInt(recommendLocation.get("warehouseAreaId").toString()));
partStockVos.add(partStockVo);
}
return partStockVos;
}
}
单元测试代码
@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class InventoryServiceImplTest {
//mock出远程调用的服务和持久层
@InjectMocks
private InventoryService inventoryService=new InventoryServiceImpl();
@Mock
private SparePartsServiceClient sparePartsServiceClient;
@Mock
private WarehouseServiceClient warehouseServiceClient;
@Mock
private PartsStockRepository partsStockRepository;
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
@Rule
public ExpectedException thrown= ExpectedException.none();
/**
*
* Method: findPartStockVoByCheckAreaId(int checkAreaId)
*
*/
@Test
public void testFindPartStockVoByCheckAreaId(){
//传入的数据
Integer checkAreaId=4590;
//模拟远程调用后的结果
List partsStockEntities=new ArrayList<>();
Map spareParts1=new HashMap<>();
Map warehouseAreas1=new HashMap<>();
Map spareParts2=new HashMap<>();
Map warehouseAreas2=new HashMap<>();
Map recommendLocation1=new HashMap<>();
Map recommendLocation2=new HashMap<>();
PartsStockEntity p1=new PartsStockEntity();
p1.setWarehouseAreaId(4600);
p1.setPartId(10000);
p1.setWarehouseId(1);
PartsStockEntity p2=new PartsStockEntity();
p2.setWarehouseAreaId(4601);
p2.setPartId(10001);
p2.setWarehouseId(2);
partsStockEntities.add(p1);
partsStockEntities.add(p2);
spareParts1.put("sparePartCode","5037HB3300289S00201A");
spareParts1.put("id",20000);
spareParts1.put("sparePartName","胶管总成");
warehouseAreas1.put("warehouseAreaId",12345);
warehouseAreas1.put("warehouseAreaCode","SH027B1");
warehouseAreas1.put("warehouseCategoryId",59);
spareParts2.put("sparePartCode","6666HB3300289S00201B");
spareParts2.put("id",20001);
spareParts2.put("sparePartName","安全阀");
warehouseAreas2.put("warehouseAreaId",54321);
warehouseAreas2.put("warehouseAreaCode","SBSBSBS1");
warehouseAreas2.put("warehouseCategoryId",70);
recommendLocation1.put("warehouseAreaId",1);
recommendLocation1.put("warehouseAreaCode","KM-ST-01");
recommendLocation2.put("warehouseAreaId",5);
recommendLocation2.put("warehouseAreaCode","BN-ST-01");
when(partsStockRepository.findPartsStockByCheckAreaId(checkAreaId)).thenReturn(partsStockEntities);
when(sparePartsServiceClient.findSparePartById(10000)).thenReturn(spareParts1);
when(warehouseServiceClient.findWarehouseAreaById(4600)).thenReturn(warehouseAreas1);
when(sparePartsServiceClient.findSparePartById(10001)).thenReturn(spareParts2);
when(warehouseServiceClient.findWarehouseAreaById(4601)).thenReturn(warehouseAreas2);
when(warehouseServiceClient.findWarehouseAreaByIdAndAreaKind(1,2)).thenReturn(recommendLocation1);
when(warehouseServiceClient.findWarehouseAreaByIdAndAreaKind(2,2)).thenReturn(recommendLocation2);
Listlist=inventoryService.findPartStockVoByCheckAreaId(checkAreaId);
//verify来判断被MOCK的服务是否被调用过
verify(partsStockRepository,atLeastOnce()).findPartsStockByCheckAreaId(checkAreaId);
verify(sparePartsServiceClient,atLeastOnce()).findSparePartById(10000);
verify(warehouseServiceClient,atLeastOnce()).findWarehouseAreaById(4600);
verify(sparePartsServiceClient,atLeastOnce()).findSparePartById(10001);
verify(warehouseServiceClient,atLeastOnce()).findWarehouseAreaById(4601);
PartStockVo vo1=list.get(0);
PartStockVo vo2=list.get(1);
//断言
Assert.assertEquals(vo1.getSparePartId(),10000);
Assert.assertEquals(vo1.getRecommendedLocation(),"KM-ST-01");
Assert.assertEquals(vo2.getSparePartName(),"安全阀");
Assert.assertEquals(vo2.getRecommendedLocation(),"BN-ST-01");
}
}