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

Mockito 避坑指南 - 常見錯誤的預(yù)防與處理

開發(fā) 測試
在本文中,我們將討論開發(fā)人員在 Spring Boot 應(yīng)用程序中使用 Mockito 框架時犯的常見錯誤,以及代碼示例和解釋。

介紹

Mockito 是一個流行的用于測試 Java 應(yīng)用程序的框架。它提供了一種強大且易于使用的方式來模擬依賴關(guān)系和編寫單元測試。然而,剛接觸 Mockito 的開發(fā)人員可能會犯一些錯誤,從而導(dǎo)致測試不可靠,甚至導(dǎo)致應(yīng)用程序出現(xiàn)意外行為。在本文中,我們將討論開發(fā)人員在 Spring Boot 應(yīng)用程序中使用 Mockito 框架時犯的常見錯誤,以及代碼示例和解釋。

1.濫用@Mock和@InjectMocks注釋

開發(fā)人員在使用 Mockito 時最常見的錯誤之一是濫用@Mock和@InjectMocks注釋。@Mock注解用于為特定類創(chuàng)建模擬對象,而@InjectMocks注解用于將模擬對象注入到被測試的類中。需要注意的是,@InjectMocks 只能與類一起使用,不能與接口一起使用。

例子:

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    
    @Mock
    private MyRepository myRepository;
    
    @InjectMocks
    private MyService myService;
    
    // test methods
    
}

2.不重置Mock對象

Mockito 可創(chuàng)建在多個測試中重用的Mock對象。如果在測試之間未重置Mock對象,則可能會導(dǎo)致意外行為和不可靠的測試。Mockito 提供了一個名為Mockito.reset()的方法,可用于重置所有Mock對象。

例子:

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void test1() {
    Mockito.when(myRepository.findById(1)).thenReturn(Optional.of(new MyObject()));
    // test code
}

@Test
public void test2() {
    Mockito.when(myRepository.findById(2)).thenReturn(Optional.of(new MyObject()));
    // test code
}

@After
public void tearDown() {
    Mockito.reset(myRepository);
}

3.對Mock對象使用錯誤的范圍

Mockito 默認(rèn)創(chuàng)建范圍為類級別。這意味著同一個Mock對象將用于類中的所有測試方法。但是,如果模擬對象需要為每個測試方法具有不同的狀態(tài)或行為,則應(yīng)使用方法級別的范圍來創(chuàng)建。要創(chuàng)建具有正確范圍的Mock對象,我們可以使用Spring Boot 提供的@MockBean注解。

@MockBean使用示例:

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetUserById() throws Exception {
        // arrange
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setName("John Doe");
        Mockito.when(userService.getUserById(userId)).thenReturn(user);

        // act
        MvcResult result = mockMvc.perform(get("/users/{id}", userId))
                .andExpect(status().isOk())
                .andReturn();

        // assert
        String response = result.getResponse().getContentAsString();
        assertThat(response).isEqualTo("{\"id\":1,\"name\":\"John Doe\"}");
        Mockito.verify(userService, times(1)).getUserById(userId);
    }

    @Test
    public void testAddUser() throws Exception {
        // arrange
        User user = new User();
        user.setName("Jane Doe");
        Mockito.when(userService.addUser(user)).thenReturn(user);

        // act
        MvcResult result = mockMvc.perform(post("/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\":\"Jane Doe\"}"))
                .andExpect(status().isOk())
                .andReturn();

        // assert
        String response = result.getResponse().getContentAsString();
        assertThat(response).isEqualTo("{\"id\":null,\"name\":\"Jane Doe\"}");
        Mockito.verify(userService, times(1)).addUser(user);
    }
}

在這個例子中,我們使用@WebMvcTest注解來測試UserController類,并注入MockMvc對象來模擬HTTP請求。我們還使用@MockBean注釋為UserService和UserRepository類創(chuàng)建模擬對象。

注意,這里不需要在測試之間重置Mock對象,因為@MockBean注解會為每個測試方法創(chuàng)建Mock對象的新實例。

4.不驗證Mock對象

Mockito 提供了 Mockito.verify()的方法,可用于驗證是否使用特定參數(shù)調(diào)用了Mock對象。如果Mock對象未經(jīng)驗證,可能會導(dǎo)致不可靠的測試和意外的行為。

Mockito.verify()使用示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetUserById() {
        // arrange
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setName("John Doe");
        Mockito.when(userRepository.findById(userId)).thenReturn(Optional.of(user));

        // act
        User result = userService.getUserById(userId);

        // assert
        assertThat(result).isEqualTo(user);
        Mockito.verify(userRepository, times(1)).findById(userId);
    }

    @Test
    public void testGetUserByIdNotFound() {
        // arrange
        Long userId = 1L;
        Mockito.when(userRepository.findById(userId)).thenReturn(Optional.empty());

        // act
        UserNotFoundException exception = assertThrows(UserNotFoundException.class, () -> {
            userService.getUserById(userId);
        });

        // assert
        assertThat(exception.getMessage()).isEqualTo("User not found with ID: " + userId);
        Mockito.verify(userRepository, times(1)).findById(userId);
    }
}

請注意,我們使用該Mockito.verify()方法來驗證兩個測試方法是否使用正確的 ID 并僅調(diào)用了該類的findById()方法一次。使用times(1)參數(shù)來指定該方法應(yīng)該被調(diào)用一次,并傳入正確的 ID 作為參數(shù)。如果未使用正確的 ID 調(diào)用該方法,或者多次調(diào)用該方法,則測試將失敗。

5.不指定Mock對象的行為

Mockito 默認(rèn)創(chuàng)建Mock對象,默認(rèn)行為是“不執(zhí)行任何操作”。這意味著,如果在Mock對象上調(diào)用方法并且未指定任何行為,則該方法將僅返回 null 或其返回類型的默認(rèn)值。指定Mock對象的行為來確保它們在測試中按預(yù)期運行非常重要。下面是使用Mockito.when()方法指定Mock對象的行為的示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetAllUsers() {
        // arrange
        List<User> users = Arrays.asList(
                new User(1L, "John Doe"),
                new User(2L, "Jane Doe")
        );
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
    }

    @Test
    public void testGetAllUsersEmpty() {
        // arrange
        List<User> users = Collections.emptyList();
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
    }
}

6.使用錯誤的方法驗證模擬對象

Mockito 提供了幾種方法來驗證是否使用特定參數(shù)調(diào)用了Mock對象,例如Mockito.verify()、Mockito.verifyZeroInteractions () 和Mockito.verifyNoMoreInteractions () 。使用正確的方法進(jìn)行所需的驗證非常重要,因為使用錯誤的方法可能會導(dǎo)致不可靠的測試和意外的行為。Mockito.verify()方法使用示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetAllUsers() {
        // arrange
        List<User> users = Arrays.asList(
                new User(1L, "John Doe"),
                new User(2L, "Jane Doe")
        );
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
        Mockito.verify(userRepository).findAll();
        Mockito.verifyNoMoreInteractions(userRepository);
    }

    @Test
    public void testEmptyUserList() {
        // arrange
        List<User> users = Collections.emptyList();
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
        Mockito.verify(userRepository).findAll();
        Mockito.verifyNoMoreInteractions(userRepository);
        Mockito.verifyZeroInteractions(userRepository);
    }
}

在第二個測試用例中,我們使用Mockito.verifyZeroInteractions()方法來驗證測試期間沒有與Mock對象發(fā)生交互。這確保只測試我們想要測試的行為,并且代碼中不會發(fā)生意外的交互。

7.不處理異常

以下是使用 Mockito 時如何處理異常的示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetUserById() {
        // arrange
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setName("John Doe");
        Mockito.when(userRepository.findById(userId)).thenReturn(Optional.of(user));

        // act
        User result = userService.getUserById(userId);

        // assert
        assertThat(result).isEqualTo(user);
    }

    @Test
    public void testGetUserByIdNotFound() {
        // arrange
        Long userId = 1L;
        Mockito.when(userRepository.findById(userId)).thenReturn(Optional.empty());

        // act and assert
        UserNotFoundException exception = assertThrows(UserNotFoundException.class, () -> {
            userService.getUserById(userId);
        });

        assertThat(exception.getMessage()).isEqualTo("User not found with ID: " + userId);
    }
}

在testGetUserByIdNotFound()方法中,我們Mock UserRepository 類的 findById() 方法以返回一個空的可選值。然后,我們使用特定 ID 調(diào)用UserService類的getUserById()方法,并且期望該方法拋出UserNotFoundException. 然后使用assertThrows()方法來驗證是否拋出了正確的異常,并且我們還使用getMessage()異常的方法來驗證是否返回了正確的消息。

8.不使用正確的匹配器

以下是使用 Mockito 時如何使用正確匹配器的示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testAddUser() {
        // arrange
        User user = new User();
        user.setName("John Doe");
        user.setAge(30);

        // act
        userService.addUser(user);

        // assert
        ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
        Mockito.verify(userRepository).save(captor.capture());
        assertThat(captor.getValue().getName()).isEqualTo("John Doe");
        assertThat(captor.getValue().getAge()).isEqualTo(30);
    }
}

使用ArgumentCaptor類來捕獲傳遞給UserRepository類的save()方法的參數(shù)值。我們還使用Mockito.eq()方法來指定方法調(diào)用的參數(shù)值,使用user.getName()和user.getAge()方法來獲取正確的值。這有助于確保向方法傳遞正確的參數(shù),并避免在測試中出現(xiàn)意外的行為。

下面是另一個示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testDeleteUserById() {
        // arrange
        Long userId = 1L;

        // act
        userService.deleteUserById(userId);

        // assert
        Mockito.verify(userRepository, Mockito.times(1)).deleteById(Mockito.eq(userId));
    }
}

使用Mockito.eq()方法來指定deleteById()方法調(diào)用的參數(shù)值。這確保了正確的ID被傳遞給該方法,并避免了測試中的意外行為。

9.沒有對Mock對象使用正確的注解

以下是使用@MockBean 和 @RunWith 注解示例:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetAllUsers() {
        // arrange
        List<User> users = Arrays.asList(
                new User(1L, "John Doe"),
                new User(2L, "Jane Doe")
        );
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
    }
}

使用@RunWith和@SpringBootTest注解來配置單元測試的Spring測試框架。通過使用這些注解,我們可以確保應(yīng)用程序上下文被加載并且依賴項被正確地注入。

10.未使用正確的測試配置

我們希望使用正確的配置,以確保正確加載應(yīng)用程序上下文并按預(yù)期注入依賴項。以下是使用@ContextConfiguration 的示例:

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(classes = {UserService.class, UserRepository.class})
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetAllUsers() {
        // arrange
        List<User> users = Arrays.asList(
                new User(1L, "John Doe"),
                new User(2L, "Jane Doe")
        );
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
    }
}

使用@ContextConfiguration注解來指定測試的配置。我們將一個類數(shù)組傳遞給它,其中包括UserService和UserRepository類,這樣可以確保它們被加載到應(yīng)用程序上下文中。

11.沒有使用正確的方法來創(chuàng)建Mock對象

使用正確的方法來創(chuàng)建Mock對象,以確保依賴項的行為是可控的并且測試是可靠的。以下是使用Mockito.mock()的示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    private UserService userService;

    private UserRepository userRepository;

    @Before
    public void setUp() {
        userRepository = Mockito.mock(UserRepository.class);
        userService = new UserService(userRepository);
    }

    @Test
    public void testGetAllUsers() {
        // arrange
        List<User> users = Arrays.asList(
                new User(1L, "John Doe"),
                new User(2L, "Jane Doe")
        );
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
    }
}

使用了Mockito.when()方法來指定Mock對象的行為,即當(dāng)findAll()方法被調(diào)用時,返回一個User對象的列表。

12.沒有使用正確的方法來存根Mock對象

使用正確的方法來存根Mock對象,以確保依賴項的行為可以控制并且測試是可靠的。以下是使用when().thenReturn()的示例:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    public void testGetAllUsers() {
        // arrange
        List<User> users = Arrays.asList(
                new User(1L, "John Doe"),
                new User(2L, "Jane Doe")
        );
        Mockito.when(userRepository.findAll()).thenReturn(users);

        // act
        List<User> result = userService.getAllUsers();

        // assert
        assertThat(result).isEqualTo(users);
    }
}

通過使用Mockito提供的when().thenReturn()方法,我們可以指定模擬對象的行為并確保在測試中控制依賴項。

13.沒有使用正確的方法來驗證Mock對象的交互

Mockito 提供了幾種驗證 Mock對象交互的方法,例如Mockito.verify()、Mockito.verifyZeroInteractions()和Mockito.verifyNoMoreInteractions()。使用正確的方法來實現(xiàn)所需的行為非常重要,因為使用錯誤的方法可能會導(dǎo)致不可靠的測試和意外的行為。

@Test
public void test() {
    MyObject myObject = new MyObject();
    myObject.setName("Name");
    Mockito.when(myRepository.findById(1)).thenReturn(Optional.of(myObject));
    
    MyObject result = myService.findById(1);
    
    Mockito.verify(myRepository).findById(1);
    Mockito.verifyNoMoreInteractions(myRepository);
    Assert.assertEquals("Name", result.getName());
}

14.沒有使用正確的方法來驗證Mock對象的交互順序

Mockito 提供了一個名為Mockito.inOrder()的方法,可用于驗證與模擬對象交互的順序。在驗證交互順序時使用此方法非常重要。

@Test
public void test() {
    MyObject myObject1 = new MyObject();
    myObject1.setName("Name 1");
    MyObject myObject2 = new MyObject();
    myObject2.setName("Name 2");
    InOrder inOrder = Mockito.inOrder(myRepository);
    
    Mockito.when(myRepository.findById(1)).thenReturn(Optional.of(myObject1));
    Mockito.when(myRepository.findById(2)).thenReturn(Optional.of(myObject2));
    
    MyObject result1 = myService.findById(1);
    MyObject result2 = myService.findById(2);
    
    inOrder.verify(myRepository).findById(1);
    inOrder.verify(myRepository).findById(2);
    Assert.assertEquals("Name 1", result1.getName());
    Assert.assertEquals("Name 2", result2.getName());
}

結(jié)論

Mockito 是一個強大的測試框架。但是,剛接觸 Mockito 的開發(fā)人員可能會犯錯誤,從而導(dǎo)致應(yīng)用程序中的測試不可靠和出現(xiàn)意外行為。

責(zé)任編輯:華軒 來源: 今日頭條
相關(guān)推薦

2024-04-03 12:30:00

C++開發(fā)

2025-03-26 02:00:00

API工具開發(fā)

2019-10-17 09:58:01

深度學(xué)習(xí)編程人工智能

2021-02-26 00:46:11

CIO數(shù)據(jù)決策數(shù)字化轉(zhuǎn)型

2024-04-24 13:45:00

2022-01-23 14:29:25

C語言編程語言

2020-12-16 10:00:59

Serverless數(shù)字化云原生

2018-01-20 20:46:33

2021-02-22 17:00:31

Service Mes微服務(wù)開發(fā)

2021-05-08 12:30:03

Pythonexe代碼

2022-03-04 18:11:16

信服云

2023-05-24 10:06:42

多云實踐避坑

2021-05-07 21:53:44

Python 程序pyinstaller

2024-08-26 08:29:55

2024-12-31 15:52:43

2020-06-12 11:03:22

Python開發(fā)工具

2021-04-28 09:26:25

公有云DTS工具

2024-07-04 09:05:30

2025-03-13 06:50:50

2020-08-26 07:37:25

Nacos微服務(wù)SpringBoot
點贊
收藏

51CTO技術(shù)棧公眾號