轉載:https://blog.csdn.net/qq_35915384/article/details/80227297
前言
JUnit 是一個回歸測試架構,被開發者用于實施對應用程式的單元測試,加快程式編制速度,同時提高編碼的品質。
JUnit 測試架構具有以下重要特性:
測試工具
測試套件
測試運作器
測試分類
了解 Junit 基礎方法
加入依賴
在 pom.xml 中加入依賴:
junit junit test 4.12 1 2 3 4 5 6 建立測試類和測試方法
測試類的的命名規則一般是 xxxTest.java ;
測試類中測試的方法可以有字首,這個看統一标準,是以有時候會發現别人的測試方法上有test字首;
并且測試方法上加上注解 @Test。
使用 IDEA 中,選中目前類名,使用快捷鍵 ALT + ENTER(WIN),向下選則 Create Test 回車,即可進入生成測試類的選項中,再次回車,就快速的生成測試類。
OK 完你會發現,生成的測試類在 src/test 目錄下,測試類和源代碼的包名 是一緻的。生成後結果(注意下生成的方法名是不加 test):
public class HelloServiceImplTest {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void say() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
JUnit中的注解
@BeforeClass:針對所有測試,隻執行一次,且必須為static void
@Before:初始化方法,執行目前測試類的每個測試方法前執行。
@Test:測試方法,在這裡可以測試期望異常和逾時時間
@After:釋放資源,執行目前測試類的每個測試方法後執行
@AfterClass:針對所有測試,隻執行一次,且必須為static void
@Ignore:忽略的測試方法(隻在測試類的時候生效,單獨執行該測試方法無效)
@RunWith:可以更改測試運作器 ,預設值 org.junit.runner.Runner
一個單元測試類執行順序為:
@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
每一個測試方法的調用順序為:
@Before –> @Test –> @After
逾時測試
如果一個測試用例比起指定的毫秒數花費了更多的時間,那麼 Junit 将自動将它标記為失敗。timeout 參數和 @Test注釋一起使用。現在讓我們看看活動中的 @test(timeout)。
@Test(timeout = 1000)
public void testTimeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
System.out.println("Complete");
}
1
2
3
4
5
上面測試會失敗,在一秒後會抛出異常 org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds
異常測試
你可以測試代碼是否它抛出了想要得到的異常。expected 參數和 @Test 注釋一起使用。現在讓我們看看活動中的 @Test(expected)。
@Test(expected = NullPointerException.class)
public void testNullException() {
throw new NullPointerException();
}
1
2
3
4
上面代碼會測試成功。
套件測試
public class TaskOneTest {
@Test
public void test() {
System.out.println(“Task one do.”);
}
}
public class TaskTwoTest {
@Test
public void test() {
System.out.println(“Task two do.”);
}
}
public class TaskThreeTest {
@Test
public void test() {
System.out.println(“Task Three.”);
}
}
@RunWith(Suite.class) // 1. 更改測試運作方式為 Suite
// 2. 将測試類傳入進來
@Suite.SuiteClasses({TaskOneTest.class, TaskTwoTest.class, TaskThreeTest.class})
public class SuitTest {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
參數化測試
Junit 4 引入了一個新的功能參數化測試。參數化測試允許開發人員使用不同的值反複運作同一個測試。你将遵循 5 個步驟來建立參數化測試。
用 @RunWith(Parameterized.class)來注釋 test 類。
建立一個由 @Parameters 注釋的公共的靜态方法,它傳回一個對象的集合(數組)來作為測試資料集合。
建立一個公共的構造函數,它接受和一行測試資料相等同的東西。
為每一列測試資料建立一個執行個體變量。
用執行個體變量作為測試資料的來源來建立你的測試用例。
//1.更改預設的測試運作器為RunWith(Parameterized.class)
@RunWith(Parameterized.class)
public class ParameterTest {
// 2.聲明變量存放預期值和測試資料
private String firstName;
private String lastName;
//3.聲明一個傳回值 為Collection的公共靜态方法,并使用@Parameters進行修飾
@Parameterized.Parameters //
public static List<Object[]> param() {
// 這裡我給出兩個測試用例
return Arrays.asList(new Object[][]{{"Mike", "Black"}, {"Cilcln", "Smith"}});
}
//4.為測試類聲明一個帶有參數的公共構造函數,并在其中為之聲明變量指派
public ParameterTest(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// 5. 進行測試,發現它會将所有的測試用例測試一遍
@Test
public void test() {
String name = firstName + " " + lastName;
assertThat("Mike Black", is(name));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Hamcrest
JUnit 4.4 結合 Hamcrest 提供了一個全新的斷言文法——assertThat。
文法:
assertThat( [actual], [matcher expected] );
1
assertThat 使用了 Hamcrest 的 Matcher 比對符,使用者可以使用比對符規定的比對準則精确的指定一些想設定滿足的條件,具有很強的易讀性,而且使用起來更加靈活。
具體使用的一些比對規則可以檢視源碼。
Spring Boot 中使用 JUnit
Spring 架構提供了一個專門的測試子產品(spring-test),用于應用程式的內建測試。 在 Spring Boot 中,你可以通過spring-boot-starter-test啟動器快速開啟和使用它。
加入依賴
org.springframework.boot spring-boot-starter-test test 1 2 3 4 5 Spring Boot 測試
// 擷取啟動類,加載配置,确定裝載 Spring 程式的裝載方法,它回去尋找 主配置啟動類(被 @SpringBootApplication 注解的)
@SpringBootTest
// 讓 JUnit 運作 Spring 的測試環境, 獲得 Spring 環境的上下文的支援
@RunWith(SpringRunner.class)
public class EmployeeServiceImplTest {
// do
}
1
2
3
4
5
6
7
Spring MVC 測試
當你想對 Spring MVC 控制器編寫單元測試代碼時,可以使用@WebMvcTest注解。它提供了自配置的 MockMvc,可以不需要完整啟動 HTTP 伺服器就可以快速測試 MVC 控制器。
需要測試的 Controller:
@RestController
@RequestMapping(value = “/emp”, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class EmployeeController {
private final EmployeeService employeeService;
@Autowired
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
@GetMapping
public ResponseEntity<List> listAll() {
return ResponseEntity.ok(employeeService.findEmployee());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
編寫 MockMvc 的測試類:
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeController.class)
public class EmployeeController2Test {
@Autowired
private MockMvc mvc;
@MockBean
private EmployeeService employeeService;
public void setUp() {
// 資料打樁,設定該方法傳回的 body一直 是空的
Mockito.when(employeeService.findEmployee()).thenReturn(new ArrayList<>());
}
@Test
public void listAll() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/emp"))
.andExpect(status().isOk()) // 期待傳回狀态嗎碼200
// JsonPath expression https://github.com/jayway/JsonPath
//.andExpect(jsonPath("$[1].name").exists()) // 這裡是期待傳回值是數組,并且第二個值的 name 存在,是以這裡測試是失敗的
.andDo(print()); // 列印傳回的 http response 資訊
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
使用@WebMvcTest注解時,隻有一部分的 Bean 能夠被掃描得到,它們分别是:
@Controller
@ControllerAdvice
@JsonComponent
Filter
WebMvcConfigurer
HandlerMethodArgumentResolver
其他正常的@Component(包括@Service、@Repository等)Bean 則不會被加載到 Spring 測試環境上下文中。
是以我在上面使用了資料打樁,Mockito 在這篇文章最後一節。
我們也可以注入Spring 上下文的環境到 MockMvc 中,如下編寫 MockMvc 的測試類:
@RunWith(SpringRunner.class)
@SpringBootTest
public class EmployeeControllerTest {
@Autowired
private WebApplicationContext ctx;
private MockMvc mockMvc;
@Before
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
@Test
public void listAll() throws Exception {
mockMvc
.perform(get("/emp") // 測試的相對位址
.accept(MediaType.APPLICATION_JSON_UTF8) // accept response content type
)
.andExpect(status().isOk()) // 期待傳回狀态嗎碼200
// JsonPath expression https://github.com/jayway/JsonPath
.andExpect(jsonPath("$[1].name").exists()) // 這裡是期待傳回值是數組,并且第二個值的 name 存在
.andDo(print()); // 列印傳回的 http response 資訊
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
值得注意的是需要首先使用 WebApplicationContext 建構 MockMvc。
Spring Boot Web 測試
當你想啟動一個完整的 HTTP 伺服器對 Spring Boot 的 Web 應用編寫測試代碼時,可以使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)注解開啟一個随機的可用端口。Spring Boot 針對 REST 調用的測試提供了一個 TestRestTemplate 模闆,它可以解析連結伺服器的相對位址。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class EmployeeController1Test {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void listAll() {
ResponseEntity<List> result = restTemplate.getForEntity("/emp", List.class);
Assert.assertThat(result.getBody(), Matchers.notNullValue());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
其實之前上面的測試傳回結果不是很正确,隻能接收個List,給測試代碼添加了不少麻煩,還好最終找到了解決辦法:
@Test
public void listAll() {
// 由于我傳回的是 List 類型的,一直想不到辦法解決,網上給出了解決辦法,使用 exchange 函數代替
//public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
// HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType,
// Object... urlVariables) throws RestClientException {
ParameterizedTypeReference<List<EmployeeResult>> type = new ParameterizedTypeReference<List<EmployeeResult>>() {};
ResponseEntity<List<EmployeeResult>> result = restTemplate.exchange("/emp", HttpMethod.GET, null, type);
Assert.assertThat(result.getBody().get(0).getName(), Matchers.notNullValue());
}
1
2
3
4
5
6
7
8
9
10
Spring Data JPA 測試
我們可以使用 @DataJpaTest注解表示隻對 JPA 測試;@DataJpaTest注解它隻掃描@EntityBean 和裝配 Spring Data JPA 存儲庫,其他正常的@Component(包括@Service、@Repository等)Bean 則不會被加載到 Spring 測試環境上下文。
@DataJpaTest 還提供兩種測試方式:
使用記憶體資料庫 h2database,Spring Data Jpa 測試預設采取的是這種方式;
使用真實環境的資料庫。
使用記憶體資料庫測試
預設情況下,@DataJpaTest使用的是記憶體資料庫進行測試,你無需配置和啟用真實的資料庫。隻需要在 pom.xml 配置檔案中聲明如下依賴即可:
com.h2database h2 1 2 3 4 gradle file:
testCompile(‘com.h2database:h2’)
1
編寫測試方法:
@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeDaoTest {
@Autowired
private EmployeeDao employeeDao;
@Test
public void testSave() {
Employee employee = new Employee();
EmployeeDetail detail = new EmployeeDetail();
detail.setName(“kronchan”);
detail.setAge(24);
employee.setDetail(detail);
assertThat(detail.getName(), Matchers.is(employeeDao.save(employee).getDetail().getName()));;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
使用真實資料庫測試
如要需要使用真實環境中的資料庫進行測試,需要替換掉預設規則,使用@AutoConfigureTestDatabase(replace = Replace.NONE)注解:
@RunWith(SpringRunner.class)
@DataJpaTest
// 加入 AutoConfigureTestDatabase 注解
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class EmployeeDaoTest {
@Autowired
private EmployeeDao employeeDao;
@Test
public void testSave() {
Employee employee = new Employee();
EmployeeDetail detail = new EmployeeDetail();
detail.setName("kronchan");
detail.setAge(24);
employee.setDetail(detail);
assertThat(detail.getName(), Matchers.is(employeeDao.save(employee).getDetail().getName()));;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
事務控制
執行上面的新增資料的測試,發現測試通過,但是資料庫卻并沒有新增資料。預設情況下,在每個 JPA 測試結束時,事務會發生復原。這在一定程度上可以防止測試資料污染資料庫。
如果你不希望事務發生復原,你可以使用@Rollback(false)注解,該注解可以标注在類級别做全局的控制,也可以标注在某個特定不需要執行事務復原的方法級别上。
也可以顯式的使用注解 @Transactional 設定事務和事務的控制級别,放大事務的範圍。
Mockito
這部分參考 使用Mockito和SpringTest進行單元測試
JUnit和SpringTest,基本上可以滿足絕大多數的單元測試了,但是,由于現在的系統越來越複雜,互相之間的依賴越來越多。特别是微服務化以後的系統,往往一個子產品的代碼需要依賴幾個其他子產品的東西。是以,在做單元測試的時候,往往很難構造出需要的依賴。一個單元測試,我們隻關心一個小的功能,但是為了這個小的功能能跑起來,可能需要依賴一堆其他的東西,這就導緻了單元測試無法進行。是以,我們就需要再測試過程中引入Mock測試。
所謂的Mock測試就是在測試過程中,對于一些不容易構造的、或者和這次單元測試無關但是上下文又有依賴的對象,用一個虛拟的對象(Mock對象)來模拟,以便單元測試能夠進行。
比如有一段代碼的依賴為:
當我們要進行單元測試的時候,就需要給A注入B和C,但是C又依賴了D,D又依賴了E。這就導緻了,A的單元測試很難得進行。
但是,當我們使用了Mock來進行模拟對象後,我們就可以把這種依賴解耦,隻關心A本身的測試,它所依賴的B和C,全部使用Mock出來的對象,并且給MockB和MockC指定一個明确的行為。就像這樣:
是以,當我們使用Mock後,對于那些難以建構的對象,就變成了個模拟對象,隻需要提前的做Stubbing(樁)即可,所謂做樁資料,也就是告訴Mock對象,當與之互動時執行何種行為過程。比如當調用B對象的b()方法時,我們期望傳回一個true,這就是一個設定樁資料的預期。
基礎
Mockito 簡明教程
Spring Boot 中使用
上面的 Spring MVC 測試 中也使用到了 Mockito,
spring-boot-starter-test 自帶了 mockito-core。
基礎業務
@Entity
@Data
@NoArgsConstructor
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false, length = 50)
private String username;
private String password;
@CreationTimestamp
private Date createDate;
public User(Long id, String username) {
this.id = id;
this.username = username;
}
}
public interface IUserRepository extends JpaRepository<User, Long> {
boolean updateUser(User user);
}
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserServiceImpl implements IUserService {
private final IUserRepository userRepository;
@Override
public User findOne(Long id) {
return userRepository.getOne(id);
}
@Override
public boolean updateUsername(Long id, String username) {
User user = findOne(id);
if (user == null) {
return false;
}
user.setUsername(username);
return userRepository.updateUser(user);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
測試類
public class IUserServiceTest {
private IUserService userService;
//@Mock
private IUserRepository userRepository;
@Before
public void setUp() throws Exception {
// 對所有注解了@Mock的對象進行模拟
// MockitoAnnotations.initMocks(this);
// 不使用注解,可以對單個對象進行 mock
userRepository = Mockito.mock(IUserRepository.class);
// 構造被測試對象
userService = new UserServiceImpl(userRepository);
// 打樁,建構當 userRepository的 getOne 函數執行參數為 1的時候,設定傳回的結果 User
Mockito.when(userRepository.getOne(1L)).thenReturn(new User(1L, "kronchan"));
// 打樁,建構當 userRepository的 getOne 函數執行參數為 2的時候,設定傳回的結果 null
Mockito.when(userRepository.getOne(2L)).thenReturn(null);
// 打樁,建構當 userRepository的 getOne 函數執行參數為 3的時候,設定結果抛出異常
Mockito.when(userRepository.getOne(3L)).thenThrow(new IllegalArgumentException("The id is not support"));
// 打樁,當 userRepository.updateUser 執行任何User類型的參數,傳回的結果都是true
Mockito.when(userRepository.updateUser(Mockito.any(User.class))).thenReturn(true);
}
@Test
public void testUpdateUsernameSuccess() {
long userId = 1L;
String newUsername = "new kronchan";
// 測試某個 service 的方法
boolean updated = userService.updateUsername(userId, newUsername);
// 檢查結果
Assert.assertThat(updated, Matchers.is(true));
// Verifies certain behavior <b>happened once</b>.
// mock對象一旦建立,就會自動記錄自己的互動行為。通過verify(mock).someMethod()方法,來驗證方法是否被調用。
// 驗證調用上面的service 方法後是否 userRepository.getOne(1L) 調用過,
Mockito.verify(userRepository).getOne(userId);
// 有條件可以測試下沒有被調用過的方法:
// Mockito.verify(userRepository).deleteById(userId);
// 則會測試失敗:
// Wanted but not invoked:
// userRepository.deleteById(1L);
// However, there were exactly 2 interactions with this mock:
// userRepository.getOne(1L);
// userRepository.updateUser(
// User(id=1, username=new kronchan, password=null, createDate=null)
// );
// updateUsername 函數中我們調用了已經打樁了的其他的函數,現在我們來驗證進入其他函數中的參數
//構造參數捕獲器,用于捕獲方法參數進行驗證
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
// 驗證updateUser方法是否被調用過,并且捕獲入參
Mockito.verify(userRepository).updateUser(userCaptor.capture());
// 擷取參數 updatedUser
User updatedUser = userCaptor.getValue();
// 驗證入參是否是預期的
Assert.assertThat(updatedUser.getUsername(), Matchers.is(newUsername));
//保證這個測試用例中所有被Mock的對象的相關方法都已經被Verify過了
Mockito.verifyNoMoreInteractions(userRepository);
// 如果有一個互動,但是我們沒有verify ,則會報錯,
// org.mockito.exceptions.verification.NoInteractionsWanted:
// No interactions wanted here:
// -> at com.wuwii.service.IUserServiceTest.testUpdateUsernameSuccess(IUserServiceTest.java:74)
// But found this interaction on mock 'iUserRepository':
// -> at com.wuwii.service.impl.UserServiceImpl.findOne(UserServiceImpl.java:21)
// ***
}
@Test
public void testUpdateUsernameFailed() {
long userId = 2L;
String newUsername = "new kronchan";
// 沒有經過 mock 的 updateUser 方法,它傳回的是 false
boolean updated = userService.updateUsername(userId, newUsername);
Assert.assertThat(updated, Matchers.not(true));
//驗證userRepository的getOne(2L)這個方法是否被調用過,(這個是被測試過的,此步驟通過)
Mockito.verify(userRepository).getOne(2L);
// 驗證 userRepository 的 updateUser(null)這個方法是否被調用過,(這個沒有被測試過,此步驟不通過)
//Mockito.verify(userRepository).updateUser(null);
Mockito.verifyNoMoreInteractions(userRepository);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
分析
建立MOCK的對象
我需要對 userService 進行測定,就需要模拟 userRepository 對象。
我在 setUp() 方法中,模拟對象并打樁。
模拟對象有兩種方式:
對注解了@Mock的對象進行模拟 MockitoAnnotations.initMocks(this);
對單個對象手動 mock :userRepository = Mockito.mock(IUserRepository.class);
資料打樁,除了上面我代碼上用的幾個方法,還有非常多的方法,具體可以在使用的時候看到,主要分下面幾種:
最基本的用法就是調用 when以及thenReturn方法了。他們的作用就是指定當我們調用被代理的對象的某一個方法以及參數的時候,傳回什麼值。
提供參數比對器,靈活比對參數。any()、any(Class type)、anyBoolean()、anyByte()、anyChar()、anyInt()、anyLong()等等,它支援複雜的過濾,可以使用正則 Mockito.matches(".*User$")),開頭結尾驗證endsWith(String suffix),startsWith(String prefix)、判空驗證isNotNull() isNull()
也還可以使用 argThat(ArgumentMatcher matcher),如:ArgumentMatcher隻有一個方法boolean matches(T argument);傳入入參,傳回一個boolean表示是否比對。
Mockito.argThat(argument -> argument.getUsername.length() > 6;
Mockito還提供了兩個表示行為的方法:thenAnswer(Answer<?> answer);、thenCallRealMethod();,分别表示自定義處理調用後的行為,以及調用真實的方法。這兩個方法在有些測試用例中還是很有用的。
對于同一個方法,Mockito可以是順序與次數關心的。也就是說可以實作同一個方法,第一次調用傳回一個值,第二次調用傳回一個值,甚至第三次調用抛出異常等等。隻需要連續的調用thenXXXX即可。
如果為一個傳回為Void的方法設定樁資料。上面的方法都是表示的是有傳回值的方法,而由于一個方法沒有傳回值,是以我們不能調用when方法(編譯器不允許)。是以,對于無傳回值的方法,Mockito提供了一些列的doXXXXX方法,比如:doAnswer(Answer answer)、doNothing()、doReturn(Object toBeReturned)、doThrow(Class<? extends Throwable> toBeThrown)、doCallRealMethod()。他們的使用方法其實和上面的thenXXXX是一樣的,但是when方法傳入的是Mock的對象:
/對void的方法設定模拟/
Mockito.doAnswer(invocationOnMock -> {
System.out.println(“進入了Mock”);
return null;
}).when(fileRecordDao).insert(Mockito.any());
1
2
3
4
5
當 Mockito 監視一個真實的對象的時候,我們也可以模拟這個對象的方法傳回我們設定的期望值,
List spy = spy(new LinkedList());
List spy = spy(new LinkedList());
// IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn(“foo”);
// You have to use doReturn() for stubbing
doReturn(“foo”).when(spy).get(0);
1
2
3
4
5
6
when方法參數中spy.get(0),調用的是真實list對象的get(0),這會産生 IndexOutOfBoundsException異常,是以這時需要用到 doReturn 方法來設定傳回值。
驗證測試方法的結果
使用斷言語句檢查結果。
驗證MOCK對象的調用
其實,在這裡我們如果隻是驗證方法結果的正确的話,就非常簡單,但是,在複雜的方法調用堆棧中,往往可能出現結果正确,但是過程不正确的情況。比如,updateUserName方法傳回false是有兩種可能的,一種可能是使用者沒有找到,還有一種可能就是userRepository.updateUser(userPO)傳回false。是以,如果我們隻是使用Assert.assertFalse(updated);來驗證結果,可能就會忽略某些錯誤。
是以我在上面的測試中還需要驗證指定的方法 userRepository).getOne(userId);是否運作過,而且我還使用了參數捕獲器,抓取中間的方法參數,用來驗證。
提供了verify(T mock, VerificationMode mode)方法。VerificationMode 有很多作用,
// 驗證指定方法 get(3) 沒有被調用
verify(mock, never()).get(3);
1
2
verifyZeroInteractions和verifyNoMoreInteractions 驗證所有 mock 的方法是否都調用過了。