Mockito自动化测试

概述

Mockito是根据MIT许可证发布的Java开源测试框架。该框架允许在自动化单元测试中创建测试双重对象(模拟对象),以达到测试驱动开发(TDD)或行为驱动开发(BDD)的目的,框架的名称和徽标是mojitos(一种饮料)的一种玩法。

搭建mock测试环境

三种方式:(MockitoAnnotations、RunWith、Rule)

  1. 使用MockitoAnnotations
public class MockByAnnotationTest {

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

    @Mock(answer = Answers.RETURNS_SMART_NULLS)
    private AccountDao accountDao;

    @Test
    public void testMock() {
        Account account = accountDao.findAccount("x", "123");
        System.out.println(account);
    }
}
  1. 使用RunWith
import static org.mockito.Mockito.mock;

@RunWith(MockitoJUnitRunner.class)
public class MockByRunnerTest {
    @Test
    public void testMock() {
        AccountDao accountDao = mock(AccountDao.class, Mockito.RETURNS_SMART_NULLS);
        Account account = accountDao.findAccount("x", "123");
        System.out.println(account);
    }

}
  1. 使用Rule
public class MockByRuleTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Test
    public void testMock() {
        AccountDao accountDao = Mockito.mock(AccountDao.class);
        Account account = accountDao.findAccount("x", "123");
        System.out.println(account);
    }
}

SpringBoot测试

//@Transactional
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class ArticleRestControllerTest {

    @Resource
    private MockMvc mockMvc;

    // 无法注入成功
    @Resource
    private HelloController helloController;

    @Resource
    public ArticleRestService articleRestService;

//    @Before
//    public void init() {
//        mockMvc = MockMvcBuilders.standaloneSetup(new ArticleRestController()).build();
//    }

    @Test
    public void saveArticle() throws Exception {

        String article = "{\n" +
                "    \"id\": 1,\n" +
                "    \"author\": \"zimug\",\n" +
                "    \"title\": \"测试spring boot\",\n" +
                "    \"content\": \"c\",\n" +
                "    \"createTime\": \"2017-07-16 05:23:34\",\n" +
                "    \"reader\":[{\"name\":\"Pioneer\",\"age\":18},{\"name\":\"kobe\",\"age\":37}]\n" +
                "}";
        MvcResult result = mockMvc.perform(
                MockMvcRequestBuilders.request(HttpMethod.POST, "/rest/article")
                .contentType("application/json").content(article))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.author").value("zimug"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.reader[0].age").value(18))
                .andDo(print())
                .andReturn();

        log.info(result.getResponse().getContentAsString(StandardCharsets.UTF_8));
    }
}

@SpringBootTest

//@Transactional
@Slf4j
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@WebMvcTest(value={HelloController.class, ArticleRestController.class})
public class ArticleRestControllerTest3 {
    
    @Resource
    private MockMvc mockMvc;

    @MockBean
    ArticleRestService articleRestService;

    // 无法注入成功
    @Resource
    private HelloController helloController;



//    @Before
//    public void init() {
//        mockMvc = MockMvcBuilders.standaloneSetup(new ArticleRestController()).build();
//    }

    @Test
    public void saveArticle() throws Exception {
        String article = "{\n" +
                "    \"id\": 1,\n" +
                "    \"author\": \"Pioneer\",\n" +
                "    \"title\": \"测试spring boot\",\n" +
                "    \"content\": \"c\",\n" +
                "    \"createTime\": \"2017-07-16 05:23:34\",\n" +
                "    \"reader\":[{\"name\":\"Pioneer\",\"age\":18},{\"name\":\"kobe\",\"age\":37}]\n" +
                "}";

        ObjectMapper objectMapper = new ObjectMapper();
        Article articleObj = objectMapper.readValue(article, Article.class);


        Mockito.when(articleRestService.saveArticle(articleObj)).thenReturn("OK");
//        Mockito.doReturn("OK").when(articleRestService).saveArticle(articleObj);

        Assert.assertThat(articleRestService.saveArticle(articleObj), equalTo("OK"));

        MvcResult result = mockMvc.perform(
                MockMvcRequestBuilders.request(HttpMethod.POST, "/rest/article")
                .contentType("application/json").content(article))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.author").value("Pioneer"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.reader[0].age").value(18))
                .andDo(print())
                .andReturn();

        log.info(result.getResponse().getContentAsString(StandardCharsets.UTF_8));
    }
}

区别

  1. @Mock与@Spy的区别:
  • @Mock默认不执行任何方法,有返回值得方法就默认返回null。
  • @Spy在未打桩的情况下会默认调用真实方法。
  1. @Mock与@MockBean的区别(@Spy与@SpyBean是同样的意思):
  • @Mock的作用类似于Mockito.mock(xxx.class),只是要在类上加上@RunWith(MockitoJUnitRunner.class)

  • @MockBean主要是在SpringBoot的测试类中将Mock的对象添加到Spring的上下文中,完成对象的依赖注入。

    使用场景:为了SpringBoot的Controller层,测试类上有注解@RunWith(SpringRunner.class),但为了避免注入所有的Bean,缩短测试时间,没有使用@SpringBootTest注解,而是使用@WebMvcTest。这种情况下,Service层的对象不能通过@Resource的方式注入,只能通过@MockBean将使用的Service类添加到上下文中。那为什么不能用@Mock呢?那是因为注解上面是@RunWith(SpringRunner.class)而不是@RunWith(MockitoJUnitRunner.class)。

  1. Mockito.when(ccc.xxx).thenReturn(…)和doReturn(…)when(ccc).xxx的区别
  • 如果是用@Mock的对象,使用这两者没有区别。
  • 如果是用@Spy的对象,doReturn会强行返回doReturn中的数据。而thenReturn这种模式,会调用spy对象的原有方法。当然,如果spy对象本身没有这个方法,那就会返回thenReturn中的值。
  1. @SpringBootTest、@AutoConfigureMockMvc、@RunWith(SpringRunner.class)和@WebMvcTest的使用场景
  • 只要是基于SpringFramwork测试都需要加上**@RunWith(SpringRunner.class)**。

  • @SpringBootTest、@AutoConfigureMockMvc,例如:对controller层进行测试,xxxController这个类里面通过@Resource注入了xxxService。在单元测试时,需要用@SpringBootTest使得所有的bean都注入到上下文中,然后通过@AutoConfigureMockMvc将所有controller层构建完成,其过程就包括从上下文中取得xxxService以装载到xxxController中。

  • @WebMvcTest是为了加快初始化时间,比如此时只是针对FirstController进行测试,就没必要将SecondController,ThirdController都构建完成。所以此时可通过WebMvcTest来指定具体的一个或者几个进行Contrller进行初始化,取代@SpringBootTest、@AutoConfigureMockMvc。但是在接口测试中,推荐使用@SpringBootTest、@AutoConfigureMockMvc、@RunWith(SpringRunner.class)这三个的组合。

  • 如果不依赖上下文,单元测试类上什么都可以不加,直接用MockMvc模拟HTTP请求进行测试。前提是,xxxController类里面不要嵌套注入了xxxService,这样是没办法成功构建xxxController的。即使现在用@InjectMocks来模拟一个xxxController,它也不是MockMvc针对的上下文中的xxxController。

  1. @InjectMocks
  • 针对直接调用某个对象的方法时有效,例如xxxController这个类里面通过@Resource注入了xxxService,单元测试时@Mock了xxxService,然后@InjectMock了xxxController,此时测试代码只能用xxxController.xxxMethod(xxx)进行测试。容易混淆:@InjectMocks并不能在做SpringBoot的单元测试时将xxxController注入到上下文中,注入还是通过@SpringBootTest、@AutoConfigureMockMvc、@SpringBootTest、@WebMvcTest等管理的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mockito本身并不提供自动化生成mock对象的功能,但是可以借助其他工具来实现自动化生成mock对象。其中比较常用的是使用mockito-inline和mockito-extension工具。 1. mockito-inline:是Mockito的一个扩展库,提供了一些实用的内联mock功能。其中之一就是支持自动化生成mock对象,可以通过使用@Mock注解来自动创建mock对象。示例如下: ```java import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class ExampleTest { @Mock List<String> mockList; @InjectMocks Example example; @Test public void testExample() { when(mockList.get(0)).thenReturn("mock"); assertEquals("mock", example.testMethod()); } } ``` 在这个示例中,我们使用@Mock注解自动创建了一个List类型的mock对象,并且使用@InjectMocks注解将mock对象注入到Example类中。然后,我们可以像之前一样对mock对象进行配置和测试。 2. mockito-extension:是Mockito的另一个扩展库,提供了更多的功能和选项。其中之一就是支持使用自定义名称来自动化生成mock对象。示例如下: ```java import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.STRICT_STUBS) public class ExampleTest { @Mock(name = "mockList") List<String> mockList; @Test public void testExample() { when(mockList.get(0)).thenReturn("mock"); assertEquals("mock", mockList.get(0)); } } ``` 在这个示例中,我们使用@Mock注解自动创建了一个List类型的mock对象,并指定了名称为"mockList"。然后,我们可以像之前一样对mock对象进行配置和测试。 总之,使用Mockito进行自动化生成mock对象需要借助其他工具或扩展库来实现,但是可以帮助我们更快速方便地进行测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值