当前位置:首页 > 科技  > 软件

Mockito 避坑指南 - 常见错误的预防与处理

来源: 责编: 时间:2023-11-01 17:04:47 367观看
导读介绍Mockito 是一个流行的用于测试 Java 应用程序的框架。它提供了一种强大且易于使用的方式来模拟依赖关系和编写单元测试。然而,刚接触 Mockito 的开发人员可能会犯一些错误,从而导致测试不可靠,甚至导致应用程序出现

介绍

jd928资讯网——每日最新资讯28at.com

Mockito 是一个流行的用于测试 Java 应用程序的框架。它提供了一种强大且易于使用的方式来模拟依赖关系和编写单元测试。然而,刚接触 Mockito 的开发人员可能会犯一些错误,从而导致测试不可靠,甚至导致应用程序出现意外行为。在本文中,我们将讨论开发人员在 Spring Boot 应用程序中使用 Mockito 框架时犯的常见错误,以及代码示例和解释。jd928资讯网——每日最新资讯28at.com

1.滥用@Mock和@InjectMocks注释

开发人员在使用 Mockito 时最常见的错误之一是滥用@Mock和@InjectMocks注释。@Mock注解用于为特定类创建模拟对象,而@InjectMocks注解用于将模拟对象注入到被测试的类中。需要注意的是,@InjectMocks 只能与类一起使用,不能与接口一起使用。jd928资讯网——每日最新资讯28at.com

例子:jd928资讯网——每日最新资讯28at.com

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

2.不重置Mock对象

Mockito 可创建在多个测试中重用的Mock对象。如果在测试之间未重置Mock对象,则可能会导致意外行为和不可靠的测试。Mockito 提供了一个名为Mockito.reset()的方法,可用于重置所有Mock对象。jd928资讯网——每日最新资讯28at.com

例子:jd928资讯网——每日最新资讯28at.com

@Beforepublic void setUp() {    MockitoAnnotations.initMocks(this);}@Testpublic void test1() {    Mockito.when(myRepository.findById(1)).thenReturn(Optional.of(new MyObject()));    // test code}@Testpublic void test2() {    Mockito.when(myRepository.findById(2)).thenReturn(Optional.of(new MyObject()));    // test code}@Afterpublic void tearDown() {    Mockito.reset(myRepository);}

3.对Mock对象使用错误的范围

Mockito 默认创建范围为类级别。这意味着同一个Mock对象将用于类中的所有测试方法。但是,如果模拟对象需要为每个测试方法具有不同的状态或行为,则应使用方法级别的范围来创建。要创建具有正确范围的Mock对象,我们可以使用Spring Boot 提供的@MockBean注解。jd928资讯网——每日最新资讯28at.com

@MockBean使用示例:jd928资讯网——每日最新资讯28at.com

@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类创建模拟对象。jd928资讯网——每日最新资讯28at.com

注意,这里不需要在测试之间重置Mock对象,因为@MockBean注解会为每个测试方法创建Mock对象的新实例。jd928资讯网——每日最新资讯28at.com

4.不验证Mock对象

Mockito 提供了 Mockito.verify()的方法,可用于验证是否使用特定参数调用了Mock对象。如果Mock对象未经验证,可能会导致不可靠的测试和意外的行为。jd928资讯网——每日最新资讯28at.com

Mockito.verify()使用示例:jd928资讯网——每日最新资讯28at.com

@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 并仅调用了该类的findById()方法一次。使用times(1)参数来指定该方法应该被调用一次,并传入正确的 ID 作为参数。如果未使用正确的 ID 调用该方法,或者多次调用该方法,则测试将失败。jd928资讯网——每日最新资讯28at.com

5.不指定Mock对象的行为

Mockito 默认创建Mock对象,默认行为是“不执行任何操作”。这意味着,如果在Mock对象上调用方法并且未指定任何行为,则该方法将仅返回 null 或其返回类型的默认值。指定Mock对象的行为来确保它们在测试中按预期运行非常重要。下面是使用Mockito.when()方法指定Mock对象的行为的示例:jd928资讯网——每日最新资讯28at.com

@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 提供了几种方法来验证是否使用特定参数调用了Mock对象,例如Mockito.verify()、Mockito.verifyZeroInteractions () 和Mockito.verifyNoMoreInteractions () 。使用正确的方法进行所需的验证非常重要,因为使用错误的方法可能会导致不可靠的测试和意外的行为。Mockito.verify()方法使用示例:jd928资讯网——每日最新资讯28at.com

@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对象发生交互。这确保只测试我们想要测试的行为,并且代码中不会发生意外的交互。jd928资讯网——每日最新资讯28at.com

7.不处理异常

以下是使用 Mockito 时如何处理异常的示例:jd928资讯网——每日最新资讯28at.com

@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 调用UserService类的getUserById()方法,并且期望该方法抛出UserNotFoundException. 然后使用assertThrows()方法来验证是否抛出了正确的异常,并且我们还使用getMessage()异常的方法来验证是否返回了正确的消息。jd928资讯网——每日最新资讯28at.com

8.不使用正确的匹配器

以下是使用 Mockito 时如何使用正确匹配器的示例:jd928资讯网——每日最新资讯28at.com

@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()方法的参数值。我们还使用Mockito.eq()方法来指定方法调用的参数值,使用user.getName()和user.getAge()方法来获取正确的值。这有助于确保向方法传递正确的参数,并避免在测试中出现意外的行为。jd928资讯网——每日最新资讯28at.com

下面是另一个示例:jd928资讯网——每日最新资讯28at.com

@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()方法调用的参数值。这确保了正确的ID被传递给该方法,并避免了测试中的意外行为。jd928资讯网——每日最新资讯28at.com

9.没有对Mock对象使用正确的注解

以下是使用@MockBean 和 @RunWith 注解示例:jd928资讯网——每日最新资讯28at.com

@RunWith(SpringRunner.class)@SpringBootTestpublic 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测试框架。通过使用这些注解,我们可以确保应用程序上下文被加载并且依赖项被正确地注入。jd928资讯网——每日最新资讯28at.com

10.未使用正确的测试配置

我们希望使用正确的配置,以确保正确加载应用程序上下文并按预期注入依赖项。以下是使用@ContextConfiguration 的示例:jd928资讯网——每日最新资讯28at.com

@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注解来指定测试的配置。我们将一个类数组传递给它,其中包括UserService和UserRepository类,这样可以确保它们被加载到应用程序上下文中。jd928资讯网——每日最新资讯28at.com

11.没有使用正确的方法来创建Mock对象

使用正确的方法来创建Mock对象,以确保依赖项的行为是可控的并且测试是可靠的。以下是使用Mockito.mock()的示例:jd928资讯网——每日最新资讯28at.com

@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对象的行为,即当findAll()方法被调用时,返回一个User对象的列表。jd928资讯网——每日最新资讯28at.com

12.没有使用正确的方法来存根Mock对象

使用正确的方法来存根Mock对象,以确保依赖项的行为可以控制并且测试是可靠的。以下是使用when().thenReturn()的示例:jd928资讯网——每日最新资讯28at.com

@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()方法,我们可以指定模拟对象的行为并确保在测试中控制依赖项。jd928资讯网——每日最新资讯28at.com

13.没有使用正确的方法来验证Mock对象的交互

Mockito 提供了几种验证 Mock对象交互的方法,例如Mockito.verify()、Mockito.verifyZeroInteractions()和Mockito.verifyNoMoreInteractions()。使用正确的方法来实现所需的行为非常重要,因为使用错误的方法可能会导致不可靠的测试和意外的行为。jd928资讯网——每日最新资讯28at.com

@Testpublic 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()的方法,可用于验证与模拟对象交互的顺序。在验证交互顺序时使用此方法非常重要。jd928资讯网——每日最新资讯28at.com

@Testpublic 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());}

结论

Mockito 是一个强大的测试框架。但是,刚接触 Mockito 的开发人员可能会犯错误,从而导致应用程序中的测试不可靠和出现意外行为。jd928资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-16372-0.htmlMockito 避坑指南 - 常见错误的预防与处理

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 一文了解负载均衡器、反向代理、API 网关区别

下一篇: Spring Boot 多个环境的配置方式

标签:
  • 热门焦点
  • MIX Fold3包装盒泄露 新机本月登场

    小米的全新折叠屏旗舰MIX Fold3将于本月发布,近日该机的真机包装盒在网上泄露。从图上来看,新的MIX Fold3包装盒在外观设计方面延续了之前的方案,变化不大,这也是目前小米旗舰
  • Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • 6月安卓手机好评榜:魅族20 Pro蝉联冠军

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年6月1日至6月30日,仅限国内市场。第一名:魅族20 Pro好评率:95%5月份的时候魅族20 Pro就是
  • SpringBoot中使用Cache提升接口性能详解

    环境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架从 3.1 开始,对 Spring 应用程序提供了透明式添加缓存的支持。和事务支持一样,抽象缓存允许一致地使用各
  • Temu起诉SHEIN,跨境电商战事升级

    来源 | 伯虎财经(bohuFN)作者 | 陈平安日前据外媒报道,拼多多旗下跨境电商平台Temu正对竞争对手SHEIN提起新诉讼,诉状称Shein&ldquo;利用市场支配力量强迫服装厂商与之签订独家
  • 一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    作者:Hiu 来源:互联网品牌官01 擦边少女空降热搜,幕后推手曝光被网友誉为&ldquo;纯欲天花板&rdquo;的女网红井川里予,近期因为一组哥特风照片登上热搜,引发了一场互联网世界关于
  • 华为Mate 60系列用上可变灵动岛:正式版体验将会更出色

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 7月4日见!iQOO 11S官宣:“鸡血版”骁龙8 Gen2+200W快充加持

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • 回归OPPO两年,一加赢了销量,输了品牌

    成为OPPO旗下主打性能的先锋品牌后,一加屡创佳绩。今年618期间,一加手机全渠道销量同比增长362%,凭借一加 11、一加 Ace 2、一加 Ace 2V三款爆品,一加
Top