自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

TestNG + PowerMock 單元測試

開發(fā) 測試
單元測試(Unit Testing),是指對軟件或項目中最小可測試單元進行正確性檢驗的測試工作。單元是人為規(guī)定最小可測試的功能模塊,可以是一個模塊,一個函數(shù)或者一個類。單元測試需要與模塊開發(fā)進行隔離情況下進行測試。

[[397322]]

單元測試(Unit Testing),是指對軟件或項目中最小可測試單元進行正確性檢驗的測試工作。單元是人為規(guī)定最小可測試的功能模塊,可以是一個模塊,一個函數(shù)或者一個類。單元測試需要與模塊開發(fā)進行隔離情況下進行測試。

在程序開發(fā)完成后,我們往往不能保證程序 100% 的正確,通過單元測試的編寫,我們可以通過自動化的測試程序將我們的輸入輸出程序進行定義,通過斷言來 Check 各個 Case 的結果,檢測我們的程序。以提高程序的正確性,穩(wěn)定性,可靠性,節(jié)省程序開發(fā)時間。我們在項目中主要用到的單元測試框架有 Spring-Boot-Test TestNG、PowerMock 等。

TestNG,即 Testing, Next Generation,下一代測試技術,是一套根據(jù) JUnit 和 NUnit 思想而構建的利用注釋來強化測試功能的一個測試框架,即可以用來做單元測試,也可以用來做集成測試。

PowerMock 也是一個單元測試模擬框架,它是在其它單元測試模擬框架的基礎上做出的擴展。通過提供定制的類加載器以及一些字節(jié)碼篡改技巧的應用,PowerMock 現(xiàn)了對靜態(tài)方法、構造方法、私有方法以及 Final 方法的模擬支持,對靜態(tài)初始化過程的移除等強大的功能。

常用注解

1. TestNG 注解

  • @BeforeSuite 在該套件的所有測試都運行在注釋的方法之前,僅運行一次
  • @AftereSuite 在該套件的所有測試都運行在注釋方法之后,僅運行一次
  • @BeforeClass 在調用當前類的第一個測試方法之前運行,注釋方法僅運行一次
  • @AftereClass 在調用當前類的第一個測試方法之后運行,注釋方法僅運行一次
  • @BeforeMethod 注釋方法將在每個測試方法之前運行
  • @AfterMethod 注釋方法將在每個測試方法之后運行
  • @BeforeTest 注釋的方法將在屬于test標簽內的類的所有測試方法運行之前運行
  • @AfterTest 注釋的方法將在屬于test標簽內的類的所有測試方法運行之后運行
  • @DataProvider 標記一種方法來提供測試方法的數(shù)據(jù)。注釋方法必須返回一個Object [] [],其中每個Object []可以被分配給測試方法的參數(shù)列表。要從該DataProvider接收數(shù)據(jù)的@Test方法需要使用與此注釋名稱相等的dataProvider名稱
  • @Parameters 描述如何將參數(shù)傳遞給@Test方法 ;適用于 xml 方式的參數(shù)化方式傳值
  • @Test 將類或方法標記為測試的一部分,此標記若放在類上,則該類所有公共方法都將被作為測試方法

2. PowerMock 注解

  • @Mock 注解實際上是 Mockito.mock() 方法的縮寫,我們只在測試類中使用它;
  • @InjectMocks 主動將已存在的 mock 對象注入到 bean 中, 按名稱注入, 但注入失敗不會拋出異常;
  • @Spy 封裝一個真實的對象,以便可以像其他 mock 的對象一樣追蹤、設置對象的行為;

示例代碼

1. 添加 pom.xml 依賴

以 Spring-Boot 項目為例,首先我們需要添加 TestNG + ProwerMock 依賴依賴如下:

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-test</artifactId> 
  4.     <scope>test</scope> 
  5. </dependency> 
  6. <dependency> 
  7.     <groupId>org.testng</groupId> 
  8.     <artifactId>testng</artifactId> 
  9.     <version>${testng.version}</version> 
  10.     <scope>test</scope> 
  11. </dependency> 
  12. <dependency> 
  13.     <groupId>org.powermock</groupId> 
  14.     <artifactId>powermock-api-mockito2</artifactId> 
  15.     <version>${powermock.version}</version> 
  16.     <scope>test</scope> 
  17. </dependency> 
  18. <dependency> 
  19.     <groupId>org.powermock</groupId> 
  20.     <artifactId>powermock-module-junit4</artifactId> 
  21.     <version>${powermock.version}</version> 
  22.     <scope>test</scope> 
  23. </dependency> 
  24. <dependency> 
  25.     <groupId>org.powermock</groupId> 
  26.     <artifactId>powermock-module-testng</artifactId> 
  27.     <version>${powermock.version}</version> 
  28.     <scope>test</scope> 
  29. </dependency> 

2. 增加單元測試

增加測試代碼

  1. import com.test.testng.dto.OrderDto; 
  2. import com.test.testng.dto.UserDto; 
  3. import org.mockito.*; 
  4. import org.powermock.modules.testng.PowerMockTestCase; 
  5. import org.testng.annotations.BeforeMethod; 
  6. import org.testng.annotations.Test; 
  7. import static org.junit.jupiter.api.Assertions.*; 
  8. import static org.mockito.Mockito.when
  9. public class OrderServiceTest extends PowerMockTestCase { 
  10.     @BeforeMethod 
  11.     public void before() { 
  12.         MockitoAnnotations.openMocks(this); 
  13.     } 
  14.     @InjectMocks 
  15.     private OrderService orderService; 
  16.     @Mock 
  17.     private UserService userService; 
  18.     // 正常測試 
  19.     @Test 
  20.     public void testCreateOrder() { 
  21.         //1. mock method start 
  22.         UserDto userDto = new UserDto(); 
  23.         userDto.setId(100); 
  24.         when(userService.get()).thenReturn(userDto); 
  25.         //2. call business method 
  26.         OrderDto order = orderService.createOrder(new OrderDto()); 
  27.         //3. assert 
  28.         assertEquals(order.getId(), 100); 
  29.     } 
  30.     // 異常測試 
  31.     @Test 
  32.     public void testCreateOrderEx() { 
  33.         //1. mock method start 
  34.         when(userService.get()).thenThrow(new RuntimeException()); 
  35.         Exception exception = null
  36.         try { 
  37.             //2. call business method 
  38.             orderService.createOrder(new OrderDto()); 
  39.         } catch (RuntimeException e) { 
  40.             exception = e; 
  41.         } 
  42.         //3. assert 
  43.         assertNotNull(exception); 
  44.     } 

常用 Mock 方式

1. Mock 靜態(tài)方法

  1. //靜態(tài)方法 
  2. UserDto dto = new UserDto(); 
  3. dto.setId(100000); 
  4. PowerMockito.mockStatic(UserService.class); 
  5. PowerMockito.when(UserService.loginStatic()).thenReturn(dto); 
  6. UserDto userDto = UserService.loginStatic(); 
  7. assertEquals(100000, userDto.getId().intValue()); 

2. Mock 私有屬性

  1. //字段賦值 
  2. ReflectionTestUtils.setField(orderService, "rateLimit", 99); 

3. Mock 私有方法

  1. // 模擬私有方法 
  2. MemberModifier.stub(MemberMatcher.method(UserService.class, "get1")).toReturn(new UserDto()); 
  3. // 測試私有方法 
  4. Method method = PowerMockito.method(UserService.class, "get1"Integer.class); 
  5. Object userDto = method.invoke(userService, 1); 
  6. assertTrue(userDto instanceof UserDto); 

進階使用

1. 參數(shù)化批量測試

在測試數(shù)據(jù)比較多的時候,我們可以通過 @DataProvider 生成數(shù)據(jù)源,通過 @Test(dataProvider = "xxx") 使用數(shù)據(jù), 如下所示:

  1. import com.test.testng.BaseTest; 
  2. import com.test.testng.dto.UserDto; 
  3. import org.mockito.InjectMocks; 
  4. import org.testng.annotations.DataProvider; 
  5. import org.testng.annotations.Test; 
  6. import static org.testng.Assert.assertFalse; 
  7. import static org.testng.AssertJUnit.assertTrue; 
  8. public class UserServiceTest2 extends BaseTest { 
  9.     @InjectMocks 
  10.     private UserService userService; 
  11.     // 定義數(shù)據(jù)源 
  12.     @DataProvider(name = "test"
  13.     public static Object[][] userList() { 
  14.         UserDto dto1 = new UserDto(); 
  15.         UserDto dto2 = new UserDto(); 
  16.         dto2.setSex(1); 
  17.         UserDto dto3 = new UserDto(); 
  18.         dto3.setSex(1); 
  19.         dto3.setFlag(1); 
  20.         UserDto dto4 = new UserDto(); 
  21.         dto4.setSex(1); 
  22.         dto4.setFlag(1); 
  23.         dto4.setAge(1); 
  24.         return new Object[][] {{dto1, null}, {dto2, null}, {dto3, null}, {dto4, null}}; 
  25.     } 
  26.     // 正確場景 
  27.     @Test 
  28.     public void testCheckEffectiveUser() { 
  29.         UserDto dto = new UserDto(); 
  30.         dto.setSex(1); 
  31.         dto.setFlag(1); 
  32.         dto.setAge(18); 
  33.         boolean result = userService.checkEffectiveUser(dto); 
  34.         assertTrue(result); 
  35.     } 
  36.  
  37.     // 錯誤場景 
  38.     @Test(dataProvider = "test"
  39.     public void testCheckEffectiveUser(UserDto dto, Object object) { 
  40.         boolean result = userService.checkEffectiveUser(dto); 
  41.         assertFalse(result); 
  42.     } 
  43.  

2. 復雜判斷保證測試覆蓋率

案例:

1.判斷有效用戶: 年齡大于 18 并且 sex = 1 并且 flag = 1

  1. public boolean checkEffectiveUser(UserDto dto) { 
  2.     // 判斷有效用戶: 年齡大于 18 并且 sex = 1 并且 flag = 1 
  3.     return Objects.equals(dto.getSex(), 1) && 
  4.         Objects.equals(dto.getFlag(), 1) && 
  5.         dto.getAge() != null && dto.getAge() >= 18; 

2.拆分邏輯。將其轉換為最簡單的 if ... else 語句。然后增加的單元測試,如下所示:

  1. public boolean checkEffectiveUser(UserDto dto) { 
  2.     if (!Objects.equals(dto.getSex(), 1)) { 
  3.         return false
  4.     } 
  5.     if (!Objects.equals(dto.getFlag(), 1)) { 
  6.         return false
  7.     } 
  8.     if (dto.getAge() == null) { 
  9.         return false
  10.     } 
  11.     if (dto.getAge() < 18) { 
  12.         return false
  13.     } 
  14.     return true

3.拆分后我們可以看到,咱們只需要 5 條單元測試就能做到全覆蓋。

  1. public class UserServiceTest extends BaseTest { 
  2.     @InjectMocks 
  3.     private UserService userService; 
  4.     // 覆蓋第一個 return  
  5.     @Test 
  6.     public void testCheckEffectiveUser_0() { 
  7.         UserDto dto =new UserDto(); 
  8.         boolean result = userService.checkEffectiveUser(dto); 
  9.         assertFalse(result); 
  10.     } 
  11.     // 覆蓋第二個 return  
  12.     @Test 
  13.     public void testCheckEffectiveUser_1() { 
  14.         UserDto dto =new UserDto(); 
  15.         dto.setSex(1); 
  16.         boolean result = userService.checkEffectiveUser(dto); 
  17.         assertFalse(result); 
  18.     } 
  19.     // 覆蓋第三個 return  
  20.     @Test 
  21.     public void testCheckEffectiveUser_2() { 
  22.         UserDto dto =new UserDto(); 
  23.         dto.setSex(1); 
  24.         dto.setFlag(1); 
  25.         boolean result = userService.checkEffectiveUser(dto); 
  26.         assertFalse(result); 
  27.     } 
  28.     // 覆蓋第四個 return 
  29.     @Test 
  30.     public void testCheckEffectiveUser_3() { 
  31.         UserDto dto =new UserDto(); 
  32.         dto.setSex(1); 
  33.         dto.setFlag(1); 
  34.         dto.setAge(1); 
  35.         boolean result = userService.checkEffectiveUser(dto); 
  36.         assertFalse(result); 
  37.     } 
  38.     // 覆蓋第五個 return 
  39.     @Test 
  40.     public void testCheckEffectiveUser_4() { 
  41.         UserDto dto =new UserDto(); 
  42.         dto.setSex(1); 
  43.         dto.setFlag(1); 
  44.         dto.setAge(18); 
  45.         boolean result = userService.checkEffectiveUser(dto); 
  46.         assertTrue(result); 
  47.     } 

4.單測覆蓋率檢測檢測

3. 通過斷言校驗方法參數(shù)

1.assert:斷言是 java 的一個保留字,用來對程序進行調試,后接邏輯運算表達式,如下:

  1. int a = 0, b = 1; 
  2. assert a == 0 && b == 0; 
  3. // 使用方法:javac編譯源文件,再 java -ea class文件名即可。 

2.在 Spring-Boot 中可以使用 Spring 提供的 Assert 類的方法對前端來的參數(shù)進行校驗,如:

  1. // 檢查年齡 >= 18 歲 
  2. public boolean checkUserAge(UserDto dto){ 
  3.     Assert.notNull(dto.getAge(), "用戶年齡不能為空"); 
  4.     Assert.isTrue(dto.getAge() >= 18, "用戶年齡不能小于 18 歲"); 
  5.     return Boolean.TRUE

3.如果是需要轉換為,rest api 返回的統(tǒng)一相應消息,我們可以通過:

  1. @ControllerAdvice 
  2. public class GlobalExceptionHandler { 
  3.     @ResponseBody 
  4.     @ExceptionHandler(value = IllegalArgumentException.class) 
  5.     public Response<String> handleArgError(IllegalArgumentException e){ 
  6.         return new Response().failure().message(e.getMessage()); 
  7.     } 

總結

原則上來講,在功能模塊的設計過程中我們應該遵循一下原則(參考 《軟件工程-結構化設計準則》):

  1. 模塊大小適中
  2. 合適的系統(tǒng)調用深度
  3. 多扇入、少扇出(增加復用度, 減少依賴程度)
  4. 單入口,單出口
  5. 模塊的作用域,應該在模塊內
  6. 功能應該可以預測的
  7. 高內聚,低耦合
  8. 系統(tǒng)分解有層次
  9. 較少的數(shù)據(jù)冗余

參考文檔

https://testng.org/doc/

https://github.com/powermock/powermock

https://www.netconcepts.cn/detail-41004.html

 

責任編輯:姜華 來源: 運維開發(fā)故事
相關推薦

2021-03-11 12:33:50

JavaPowerMock技巧

2024-08-21 08:22:33

2017-01-14 23:42:49

單元測試框架軟件測試

2017-01-14 23:26:17

單元測試JUnit測試

2017-01-16 12:12:29

單元測試JUnit

2020-08-18 08:10:02

單元測試Java

2017-03-23 16:02:10

Mock技術單元測試

2023-07-26 08:58:45

Golang單元測試

2011-07-04 18:16:42

單元測試

2020-05-07 17:30:49

開發(fā)iOS技術

2011-05-16 16:52:09

單元測試徹底測試

2024-10-16 16:09:32

2011-04-18 13:20:40

單元測試軟件測試

2013-06-04 09:49:04

Spring單元測試軟件測試

2017-02-23 15:59:53

測試MockSetup

2009-09-25 10:33:25

Hibernate單元

2010-01-28 15:54:19

Android單元測試

2020-09-30 08:08:15

單元測試應用

2024-07-29 12:12:59

2011-06-14 15:56:42

單元測試
點贊
收藏

51CTO技術棧公眾號