微服務框架下的單元測試

 

本文主要闡述瞭如何基於Junit和Mockito針對微服務做單元測試。

參考資料: https://www.tianmaying.com/tutorial/JunitForSpringBoot

 

Mockito常用註解如下:

 

@InjectMock

測試主類

 

@Spy

如果需要調用測試主類本身的方法時,需要用到該註解做部分模擬,

一般結合@InjectMock一起使用

 

@Mock

需要被模擬的依賴類

 

when(對象名.方法名).thenReturn(返回值)  //需要模擬依賴類的方法時使用

 

doReturn(返回值).when(測試主類).方法 //需要模擬測試主類自己的service方法時使用

 

步驟說明:

目標:測試mainService中 getData方法,
getData方法本身需要調用service1.getOne()  和 自身服務中getSubData()方法

 

 
Step1  在測試類上加以下註解:
@RunWith(MockitoJUnitRunner.class)
@SpringBootTest

 
Step2  在測試類內使用以下註解聲明依賴屬性:
@Mock
Service1  service1;

 

 
@InjectMock
@Spy
MainService  mainService;

 
Step3 編寫單元測試主體方法,結合when  和 doReturn編寫模擬預期值
此時模擬方法接收的入參和返回值需要硬編碼的方式寫好
@Test
public void testGetData(){
    when(service1.getOne()).thenReturn(返回值);  //需要模擬依賴類的方法時使用
    doReturn(返回值).when(mainService).getSubData(); //需要依賴測試主類自己的service方法時使用
    mainService.getData();

 
   //Step4 編寫單元測試驗證語句,執行單元測試
   可以結合verify 和Assert驗證語句對mainService中具體的方法進行驗證
   ...
   ....
}

 

 

1.Mock出所有需要調用的遠程服務和持久層方法,使得單元測試不依賴與其他服務和數據庫獨立運行。Mockito的用法可以參考: http://blog.csdn.net/anlegor/article/details/44452011

 

2.service中過於簡單的業務,如:只是僅僅進行增刪改查而沒有其他邏輯的方法,這種則選擇不做測試。而對於複雜的業務,可能個出現多種情況的應該每種情況做出一個單元測試。

 

3.斷言Assert:JUnit爲我們提供了一些輔助函數,他們用來幫助我們確定被測試的方法是否按照預期的效果正常工作,通常,把這些輔助函數稱爲斷言。
  單元測試的最後我們都應加上斷言,以確定我們的方法執行後的結果是我們所預期的。斷言的使用不應當僅僅是判斷是否爲null,而是應該把測試後的結果集取出來,判斷具體某個值是否和我們預期的匹配。
  如果需要驗證方法是否被mock且是否爲所執行的參數調用,可以使用verify來驗證。

 

4.代碼示例:

Service

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");
    }
}

 
5、使用Mock模擬Repository層複雜查詢方法
將所有用到findAll(Specification specification)方法改爲findAll(SearchFilterCollection searchFilterCollection);
findAll(Specification specification, Pageable pageable),findAll(Specification specification, Sort sort)類比修改
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章