本文主要闡述瞭如何基於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;
- 編寫單元測試主體方法,結合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");
}
}