學習 Spring Boot:(二十九)Spring Boot Junit 單元測試

前言

JUnit 是一個迴歸測試框架,被開發者用於實施對應用程序的單元測試,加快程序編制速度,同時提升編碼的質量。css

JUnit 測試框架具備如下重要特性:html

  • 測試工具
  • 測試套件
  • 測試運行器
  • 測試分類

瞭解 Junit 基礎方法

加入依賴

pom.xml 中加入依賴:java

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
    <version>4.12</version>
</dependency>

建立測試類和測試方法

  1. 測試類的的命名規則通常是 xxxTest.java
  2. 測試類中測試的方法能夠有前綴,這個看統一標準,因此有時候會發現別人的測試方法上有test前綴;
  3. 而且測試方法上加上註解 @Test

使用 IDEA 中,選中當前類名,使用快捷鍵 ALT + ENTER(WIN),向下選則 Create Test 回車,便可進入生成測試類的選項中,再次回車,就快速的生成測試類。git

img

OK 完你會發現,生成的測試類在 src/test 目錄下,測試類和源代碼的包名 是一致的。生成後結果(注意下生成的方法名是不加 test):github

public class HelloServiceImplTest {

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void say() {
    }
}

JUnit中的註解

  • @BeforeClass:針對全部測試,只執行一次,且必須爲static void
  • @Before:初始化方法,執行當前測試類的每一個測試方法前執行。
  • @Test:測試方法,在這裏能夠測試指望異常和超時時間
  • @After:釋放資源,執行當前測試類的每一個測試方法後執行
  • @AfterClass:針對全部測試,只執行一次,且必須爲static void
  • @Ignore:忽略的測試方法(只在測試類的時候生效,單獨執行該測試方法無效)
  • @RunWith:能夠更改測試運行器 ,缺省值 org.junit.runner.Runner

一個單元測試類執行順序爲:web

@BeforeClass –> @Before –> @Test –> @After –> @AfterClassspring

每個測試方法的調用順序爲:數據庫

@Before –> @Test –> @Afterexpress

超時測試

若是一個測試用例比起指定的毫秒數花費了更多的時間,那麼 Junit 將自動將它標記爲失敗。timeout 參數和 @Test註釋一塊兒使用。如今讓咱們看看活動中的 @test(timeout)json

@Test(timeout = 1000)
    public void testTimeout() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("Complete");
    }

上面測試會失敗,在一秒後會拋出異常 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();
    }

上面代碼會測試成功。

套件測試

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 {
    /** * 測試套件的入口類只是組織測試類一塊兒進行測試,無任何測試方法, */
}

參數化測試

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

Hamcrest

JUnit 4.4 結合 Hamcrest 提供了一個全新的斷言語法——assertThat。

語法:

assertThat( [actual], [matcher expected] );

assertThat 使用了 Hamcrest 的 Matcher 匹配符,用戶可使用匹配符規定的匹配準則精確的指定一些想設定知足的條件,具備很強的易讀性,並且使用起來更加靈活。

具體使用的一些匹配規則能夠查看源碼。

Spring Boot 中使用 JUnit

Spring 框架提供了一個專門的測試模塊(spring-test),用於應用程序的集成測試。 在 Spring Boot 中,你能夠經過spring-boot-starter-test啓動器快速開啓和使用它。

加入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Spring Boot 測試

// 獲取啓動類,加載配置,肯定裝載 Spring 程序的裝載方法,它回去尋找 主配置啓動類(被 @SpringBootApplication 註解的)
@SpringBootTest
// 讓 JUnit 運行 Spring 的測試環境, 得到 Spring 環境的上下文的支持
@RunWith(SpringRunner.class)
public class EmployeeServiceImplTest {
    // do 
}

Spring MVC 測試

當你想對 Spring MVC 控制器編寫單元測試代碼時,可使用@WebMvcTest註解。它提供了自配置的 MockMvc,能夠不須要完整啓動 HTTP 服務器就能夠快速測試 MVC 控制器

  1. 須要測試的 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<EmployeeResult>> listAll() {
           return ResponseEntity.ok(employeeService.findEmployee());
       }
    }
  2. 編寫 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 信息
       }
    }

    使用@WebMvcTest註解時,只有一部分的 Bean 可以被掃描獲得,它們分別是:

    • @Controller
    • @ControllerAdvice
    • @JsonComponent
    • Filter
    • WebMvcConfigurer
    • HandlerMethodArgumentResolver
      其餘常規的@Component(包括@Service@Repository等)Bean 則不會被加載到 Spring 測試環境上下文中。
      因此我在上面使用了數據打樁,Mockito 在這篇文章最後一節。
  3. 咱們也能夠注入Spring 上下文的環境到 MockMvc 中,以下編寫 MockMvc 的測試類:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class EmployeeControllerTest {
       /** * Interface to provide configuration for a web application. */
       @Autowired
       private WebApplicationContext ctx;
    
       private MockMvc mockMvc;
    
       /** * 初始化 MVC 的環境 */
       @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 信息
       }
    }

    值得注意的是須要首先使用 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());
    }
}

其實以前上面的測試返回結果不是很正確,只能接收個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());
    }

Spring Data JPA 測試

咱們可使用 @DataJpaTest註解表示只對 JPA 測試;@DataJpaTest註解它只掃描@EntityBean 和裝配 Spring Data JPA 存儲庫,其餘常規的@Component(包括@Service@Repository等)Bean 則不會被加載到 Spring 測試環境上下文。

@DataJpaTest 還提供兩種測試方式:

  1. 使用內存數據庫 h2database,Spring Data Jpa 測試默認採起的是這種方式;
  2. 使用真實環境的數據庫。
使用內存數據庫測試
  1. 默認狀況下,@DataJpaTest使用的是內存數據庫進行測試,你無需配置和啓用真實的數據庫。只須要在 pom.xml 配置文件中聲明以下依賴便可:

    <dependency>
       <groupId>com.h2database</groupId>
       <artifactId>h2</artifactId>
    </dependency>

    gradle file:

    testCompile('com.h2database:h2')
  2. 編寫測試方法:

    @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()));;
       }
    }
使用真實數據庫測試

如要須要使用真實環境中的數據庫進行測試,須要替換掉默認規則,使用@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()));;
    }
}

事務控制

執行上面的新增數據的測試,發現測試經過,可是數據庫卻並無新增數據。默認狀況下,在每一個 JPA 測試結束時,事務會發生回滾。這在必定程度上能夠防止測試數據污染數據庫。

若是你不但願事務發生回滾,你可使用@Rollback(false)註解,該註解能夠標註在類級別作全局的控制,也能夠標註在某個特定不須要執行事務回滾的方法級別上。

也能夠顯式的使用註解 @Transactional 設置事務和事務的控制級別,放大事務的範圍。

Mockito

這部分參考 使用Mockito和SpringTest進行單元測試

JUnitSpringTest,基本上能夠知足絕大多數的單元測試了,可是,因爲如今的系統愈來愈複雜,相互之間的依賴愈來愈多。特別是微服務化之後的系統,每每一個模塊的代碼須要依賴幾個其餘模塊的東西。所以,在作單元測試的時候,每每很難構造出須要的依賴。一個單元測試,咱們只關心一個小的功能,可是爲了這個小的功能能跑起來,可能須要依賴一堆其餘的東西,這就致使了單元測試沒法進行。因此,咱們就須要再測試過程當中引入Mock測試。

所謂的Mock測試就是在測試過程當中,對於一些不容易構造的、或者和此次單元測試無關可是上下文又有依賴的對象,用一個虛擬的對象(Mock對象)來模擬,以便單元測試可以進行。

好比有一段代碼的依賴爲:
img
當咱們要進行單元測試的時候,就須要給A注入BC,可是C又依賴了DD又依賴了E。這就致使了,A的單元測試很可貴進行。
可是,當咱們使用了Mock來進行模擬對象後,咱們就能夠把這種依賴解耦,只關心A自己的測試,它所依賴的B和C,所有使用Mock出來的對象,而且給MockBMockC指定一個明確的行爲。就像這樣:
img

所以,當咱們使用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);
    }
}
測試類
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);
    }
}
分析
建立MOCK的對象

我須要對 userService 進行測定,就須要模擬 userRepository 對象。

我在 setUp() 方法中,模擬對象並打樁。

模擬對象有兩種方式:

  1. 對註解了@Mock的對象進行模擬 MockitoAnnotations.initMocks(this);
  2. 對單個對象手動 mock :userRepository = Mockito.mock(IUserRepository.class);

數據打樁,除了上面我代碼上用的幾個方法,還有很是多的方法,具體能夠在使用的時候看到,主要分下面幾種:

  1. 最基本的用法就是調用 when以及thenReturn方法了。他們的做用就是指定當咱們調用被代理的對象的某一個方法以及參數的時候,返回什麼值。

  2. 提供參數匹配器,靈活匹配參數。any()any(Class<T> 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;

  3. Mockito還提供了兩個表示行爲的方法:thenAnswer(Answer<?> answer);thenCallRealMethod();,分別表示自定義處理調用後的行爲,以及調用真實的方法。這兩個方法在有些測試用例中仍是頗有用的。

  4. 對於同一個方法,Mockito能夠是順序與次數關心的。也就是說能夠實現同一個方法,第一次調用返回一個值,第二次調用返回一個值,甚至第三次調用拋出異常等等。只須要連續的調用thenXXXX便可。

  5. 若是爲一個返回爲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());

    當 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);

    when方法參數中spy.get(0),調用的是真實list對象的get(0),這會產生 IndexOutOfBoundsException異常,因此這時須要用到 doReturn 方法來設置返回值。

驗證測試方法的結果

使用斷言語句檢查結果。

驗證MOCK對象的調用

其實,在這裏咱們若是隻是驗證方法結果的正確的話,就很是簡單,可是,在複雜的方法調用堆棧中,每每可能出現結果正確,可是過程不正確的狀況。好比,updateUserName方法返回false是有兩種可能的,一種多是用戶沒有找到,還有一種可能就是userRepository.updateUser(userPO)返回false。所以,若是咱們只是使用Assert.assertFalse(updated);來驗證結果,可能就會忽略某些錯誤。

  1. 所以我在上面的測試中還須要驗證指定的方法 userRepository).getOne(userId);是否運行過,並且我還使用了參數捕獲器,抓取中間的方法參數,用來驗證。

  2. 提供了verify(T mock, VerificationMode mode)方法。VerificationMode 有不少做用,

    // 驗證指定方法 get(3) 沒有被調用 
       verify(mock, never()).get(3);
  3. verifyZeroInteractionsverifyNoMoreInteractions 驗證全部 mock 的方法是否都調用過了。

相關文章
相關標籤/搜索