微服务项目如何做单元测试?

 

本文主要阐述了如何基于Junit和Mockito针对微服务做单元测试。

参考资料: https://www.tianmaying.com/tutorial/JunitForSpringBoot

 

Mockito常用注解如下:

 

@InjectMock

一般在测试主类上使用

 

@Spy

如果需要调用测试主类本身的方法时,需要用到该注解做部分模拟,

一般结合@InjectMock一起使用

 

@Mock

需要被模拟的依赖类

 

when(对象名.方法名).thenReturn(返回值)  //需要模拟依赖类的方法时使用

 

doReturn(返回值).when(测试主类).方法 //需要模拟测试主类自己的service方法时使用

 

 

 


具体使用步骤:

 

  1. 在测试类上加以下注解:
    @RunWith(MockitoJUnitRunner.class)
    
    @SpringBootTest

     

  2. 在测试类内使用以下注解声明依赖属性:
    @Mock
    Service1  service1;
    
    @InjectMock
    
    @Spy
    MainService  mainService;

     

  3. 编写单元测试主体方法,结合when  和 doReturn编写模拟预期值:
    @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<PartStockVo> findPartStockVoByCheckAreaId(int checkAreaId) {
    List<PartsStockEntity> partsStockEntities = partsStockRepository.findPartsStockByCheckAreaId(checkAreaId);
    List<PartStockVo> partStockVos = new ArrayList<>();
    for (PartsStockEntity partsStockEntity : partsStockEntities) {
        PartStockVo partStockVo = TransferObject.transferObject(partsStockEntity, PartStockVo.class);
        Map<String, Object> spareParts = sparePartsServiceClient.findSparePartById(partStockVo.getSparePartId());
        partStockVo.setSparePartCode((String) spareParts.get("sparePartCode"));
        partStockVo.setSparePartName((String) spareParts.get("sparePartName"));
        Map<String, Object> warehouseAreas = warehouseServiceClient.findWarehouseAreaById(partStockVo.getCheckAreaLocationId());
        partStockVo.setCheckAreaLocation((String) warehouseAreas.get("warehouseAreaCode"));
        Map<String, Object> 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<PartsStockEntity> partsStockEntities=new ArrayList<>();
    Map<String,Object> spareParts1=new HashMap<>();
    Map<String,Object> warehouseAreas1=new HashMap<>();
    Map<String,Object> spareParts2=new HashMap<>();
    Map<String,Object> warehouseAreas2=new HashMap<>();
    Map<String,Object> recommendLocation1=new HashMap<>();
    Map<String,Object> 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);

    List<PartStockVo>list=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");
    }
}

 


版权声明:本文为ccc7574原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。