微服務項目如何做單元測試?

 

本文主要闡述瞭如何基於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");
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章