【单元测试】Mockito使用详解

这篇具有很好参考价值的文章主要介绍了【单元测试】Mockito使用详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

mockito.when,软件测试,自动化测试,功能测试
一个单元测试应该有如下特点:

  • 应该是自动化的
  • 应该可以快速运行
  • 每个单元测试不应该依赖其它测试的结果和执行顺序,单元测试框架可以按任意的顺序执行每个测试
  • 每个单元测试不应该依赖数据库,外部文件,或者任何长时间运行的任务。单元测试应该是独立的,不应该依赖于任何外部环境
  • 单元测试应该在任何时间任何环境都可以任意执行,不该依赖外部环境
  • 测试应该是有意义的,具有表达性,可以表达出这个测试是测什么,也可以从侧面反推这个被测试的方法的能力
  • 测试代码应该跟生产代码拥有同等标准要求

Mockito 是什么

为了满足单元测试的隔离性,隔离外部环境,我们需要将外部依赖,通过替身的方式,模拟出一个可以满足我们测试的假对象。

Mockito 就是可以制作假替身,代替外部依赖,模拟我们自定义的返回操作的替身测试框架。

mock替身,很像电影中的替身演员,在某些场景中我们需要替身来处理某些问题。

Mockito 如何使用

1. 设置测试类依托Mockito管理

Mock 需要先设置测试类被 Mockito 管理。有两种方式。

  1. 在测试类上添加注解 @RunWith(MockitoJUnitRunner.class)
  2. 在@Before方法中,调用 MockitoAnnotations.initMocks(this)

上述两种方法使用其一都可,效果一样。

@Before 注解是 Junit 提供的,在每个@Test测试执行前执行一次。

方式1代码示例:

@RunWith(MockitoJUnitRunner.class)
public class MockDemo {
    @Test
    public void testMock() {
        // 处理测试
    }
}

方式2代码示例:

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

2.对类构造替身对象

对类构造替身对象有如下几种方式

  1. 在测试用例中直接使用 Mockito.mock(Class) 方法,生成替身对象
  2. 在测试类中声明需生成替身的依赖类,使用 @Mock 注解。

第二种方式是该测试类中所有单元测试唯一的对象,为了防止每个单元测试 stubbing 的先后顺序对其他单元测试的影响,最好写一个 @After 方法,在该方法中使用 Mockito.reset() 方法清空 stubbing 规则。

@After 注解是 Junit 提供的,在每个@Test测试执行后执行一次。

方法1代码示例:

@RunWith(MockitoJUnitRunner.class)
public class MockDemo {
    @Test
    public void testMock() {
        // 处理测试
        ArrayList mock = mock(ArrayList.class);
    }
}

方法2代码示例:

@RunWith(MockitoJUnitRunner.class)
public class MockDemo {
    @Mock
    private List<String> list;
    
    ... 具体测试 ...
    
    // @After 每个单元测试方法结束后执行,重置 stubbing 
    @After
    public void destory() {
        Mockito.reset(list);
    }
}

Mockito 底层是对被Mock的对象使用 cglib 生成一个代理类。

3. 依赖注入被测试类

Mock 是对被测试类的依赖对象构造替身操作,还需将替身注入到被测试类。注入可以使用 @InjectMocks 注解标识需要被注入的类。依赖的其他类 就用@Mock 注解标识,Mockito 自动将替身注入到被测试类。

代码示例:

@RunWith(MockitoJUnitRunner.class)
public class MockTest {

    @InjectMocks
    private OrderService orderService;

    @Mock
    private OrderMapper orderMapper;
    
}

4. Stubbing

通过 Mock 构造出替身对象后,我们还需要设置替身对象的行为。有点类似录制与播放。比方说设置替身对象调用某方法的返回值。

设置某方法调用的返回值
最常用的一种方式,就是设置某种方法根据某个入参的返回值是什么。或者设置抛出什么异常。

  1. 设置返回对象方式一 when().thenReturn();
  2. 设置返回对象方式二 doReturn().when(对象).对象方法;
  3. 设置抛出异常 when().thenThrow();

设置正常返回代码示例:

@Test
public void testMock() {
    // 处理测试
    list = mock(ArrayList.class);
    // 设置当调用 list.get(0) 的时候,返回 “first value”
    when(list.get(0)).thenReturn("first value");
    assertThat(list.get(0), equalTo("first value"));
}

设置正常返回方式二代码示例:

@Test
public void testMock() {
    // 处理测试
    list = mock(ArrayList.class);
    // 设置当调用 list.get(0) 的时候,返回 “first value”
    Mockito.doReturn("first value").when(list).get(0);
    assertThat(list.get(0), equalTo("first value"));
}

设置抛出异常返回代码示例:

@Test
public void testMock() {
    // 设置当调用 list.get(0) 的时候,抛出异常
    when(list.get(0)).thenThrow(new RuntimeException());
    try {
        list.get(0);
        fail();
    } catch (Exception e) {
        assertThat(e, instanceOf(RuntimeException.class));
    }
}

针对抛出异常的单元测试 Junit 有更优雅的使用方式,ExpectedException。优点就是减少缩进,代码可读性高。

使用方法很简单,具体见如下Junit异常校验代码示例。都是 org.junit 包下。

  1. 声明 ExpectedException ,并添加 @Rule 注解
  2. 在 @Test 测试中,设置 expect 期望抛出什么异常,设置 expect 要在抛出异常的代码之前。

Junit异常校验代码示例:

@RunWith(MockitoJUnitRunner.class)
public class MockDemo {

    @Mock
    private List<String> list;

    @Rule
    public ExpectedException thrown = ExpectedException.none();
    
    @Test
    public void testMock() {
        thrown.expect(RuntimeException.class);
        when(list.get(0)).thenThrow(new RuntimeException());
        list.get(0);
    }
    
}

给Void类型方法设置Stubbing

void 方法,没有返回值,我们也可以设置其什么都不做,也可设置抛出异常。

  1. Void方法什么都不做,执行空逻辑
    Mockito.doNothing().when(对象).对象的方法

  2. Void方法抛出异常

执行空逻辑代码示例:

@Test
public void testMockVoid() {
    // 设置 list 对象 执行 clear 的时候 不执行实际逻辑,执行空逻辑
    Mockito.doNothing().when(list).clear();
    list.clear();
    // 验证 list 对象 对 clear 方法只执行了一次
    Mockito.verify(list, times(1)).clear();
}

抛出异常代码示例:

@Test
public void testMockVoid2() {
    thrown.expect(RuntimeException.class);
    // 设置 list 对象 执行 clear 的时候 不执行实际逻辑,抛出异常
    Mockito.doThrow(new RuntimeException()).when(list).clear();
    list.clear();
}

针对 同一个方法多次调用设置每次的返回值

有时候我们需要设置每一次调用的返回值,比方说某个方法第一次调用返回和第二次调用返回不一样。只要按顺序,只要在 thenReturn() 方法中 用逗号分隔即可,或者执行多次 thenReturn(),链式调用。

逗号分隔代码示例:

@Test
public void testMockReturnMultiple() {
    Mockito.when(list.size()).thenReturn(1, 2, 3, 4);

    assertThat(list.size(), equalTo(1));
    assertThat(list.size(), equalTo(2));
    assertThat(list.size(), equalTo(3));
    assertThat(list.size(), equalTo(4));
}

链式调用代码示例:

@Test
public void testMockReturnMultiple2() {
    Mockito.when(list.size()).thenReturn(1).thenReturn(2).thenReturn(3).thenReturn(4);

    assertThat(list.size(), equalTo(1));
    assertThat(list.size(), equalTo(2));
    assertThat(list.size(), equalTo(3));
    assertThat(list.size(), equalTo(4));
}

替换 方法 为 具体的行为

我们可以给替身对象的方法替换具体的行为,也就是修改其方法实现。

代码示例:

@Test
public void testMockByAnswer() {
    // anyInt 表示任意 int 类型入参,都处理为 answer 中的行为
    Mockito.when(list.get(anyInt())).thenAnswer(invocation -> {
        Integer index = invocation.getArgumentAt(0, Integer.class);
        return String.valueOf(index * 200);
    });

    assertThat(list.get(1), equalTo("200"));
    assertThat(list.get(5), equalTo("1000"));
}

设置 mock 对象依然调用原对象方法

默认mock 的对象的所有方法如果不 stubbing 处理,都是空方法。我们可以指定替身对象依然执行对象本身的实现。

代码示例:

@Test
public void testMockCallRealMethod() {
    // anyInt 表示任意 int 类型入参,都处理为 answer 中的行为
    ArrayList<Object> arrayList = mock(ArrayList.class);
    Mockito.when(arrayList.size()).thenReturn(1).thenCallRealMethod();

    assertThat(arrayList.size(), equalTo(1));
    assertThat(arrayList.size(), equalTo(0));
}

Spying 间谍对象

经过 spy 方法构造的对象,跟 mock 是相反的,spy 构造的方法,如果不设置 stubbing,则依然执行代理的对象的真正方法。使用方式跟 mock 一致。

spying 英文是间谍的意思,说明这不是替身,而是真正对象中一个特殊的对象,大部分方法都跟正常方法一样,只是某些方法有自己的特殊想法。

使用代码示例:

@RunWith(MockitoJUnitRunner.class)
public class SpyDemo {
    @Test
    public void testSpy() {
        // 处理测试
        ArrayList mock = Mockito.spy(ArrayList.class);
    }
}

WILDCARD MATCHERS 通用参数匹配

通用匹配。就是 any()这种任意的。有的时候我们不关注入参的值,只关心返回值,传进去什么都可以,那我们就可以选择使用 wildcard matchers 。

  1. Matchers.any() 匹配任意对象,带着 any 名称的方法 还有 anyInt,anyObject 等都是一样的作用。
  2. Matchers.eq()
    when(list.get(eq(0)) 跟 when(list.get(0) 没区别。提供这个主要是可以在通用参数匹配中,指定特殊的具体value匹配。
  3. Matchers.isA() 表示匹配参数为任意某个类及其子类的对象。

值得注意的事使用通用匹配的话 需要都用通用匹配函数,如果想指定某个参数是特殊值 用eq() 包装一下,示例如下:

@Test
public void testMock() {
    // anyInt 表示任意 int 类型入参,都处理为 answer 中的行为
    ArrayList<Object> arrayList = mock(ArrayList.class);

    // 如下 直接使用会报错,因为后边有 anyObject() 通用匹配
    // Mockito.when(arrayList.set(1, anyObject())).thenReturn("1");
    // 改写成 eq(1) 则可正常运行
    Mockito.when(arrayList.set(eq(1), anyObject())).thenReturn("1");

    assertThat(arrayList.set(1, new Object()), equalTo("1"));
}

Hamcrest Matchers

Hamcrest Matchers 是更优雅的断言,就是上述例子中的 assertThat() 系列方法,不同于 Junit 直接写的 Assert.assertEquals() 大量静态方法,hamcrest matchers 提供了类似函数式编程的可自定义策略的断言方式,允许通过函数的组合搭建出具有表达性的断言。其表达性,可扩展性更强。

assertThat 本质是提供了一个骨架,其实现是调用 Matcher 接口的方法。所以我们可以通过扩展 Matcher接口来扩展各种断言方法。

三个参数的 assertThat() 方法 第一个参数表示 reason 字符串,可以描述一下这个断言。在断言不通过时,会打印该描述。

assertThat 的源码如下,其本质是策略模式和模板方法模式:

public static <T> void assertThat(T actual, Matcher<T> matcher) {
    assertThat("", actual, matcher);
}

public static <T> void assertThat(String reason, T actual,
			Matcher<T> matcher) {
    if (!matcher.matches(actual)) {
        Description description= new StringDescription();
        description.appendText(reason);
        description.appendText("\nExpected: ");
        description.appendDescriptionOf(matcher);
        description.appendText("\n     got: ");
        description.appendValue(actual);
        description.appendText("\n");
        throw new java.lang.AssertionError(description.toString());
    }
}

示例中的类全路径 org.junit.assertThat,org.hamcrest.Matchers

常用Matcher

  1. equalTo() 实际值等于期望对象
    not() 不如何如何 可以组装 equalTo() 例如 not(equalTo()) 表示实际值不等于期望对象
    is() 跟 equalTo() 一样
    greaterThan() 实际值大于期望对象
    lessThan() 实际值小于期望对象
    either().or() 或关系,满足其一即可
    both().and() 且关系,两个条件都得满足
    anyOf() 满足其中之一即可,相当于IN
    allOf() 所有都满足 相当于一堆 AND
@Test
public void testMatcher() {
    int i = 10;
    assertThat(i, equalTo(10));

    assertThat(i, not(equalTo(20)));

    assertThat(i, is(10));

    assertThat(i, is(not(20)));
    
    assertThat(i, greaterThan(0));

    assertThat(i, lessThan(20));

    assertThat(i, either(equalTo(10)).or(equalTo(20)));

    assertThat(i, both(equalTo(10)).and(equalTo(10)));

    assertThat(i, anyOf(is(10), equalTo(30), equalTo(20)));
    
    assertThat(i, allOf(is(10), equalTo(30), equalTo(20)));
}

自定义 matchers

Hamcrest Matchers 的扩展性强,如果其默认实现的 matcher 无法满足我们的需要,我们可以自行定义 matcher。方法如下。

创建自定义Matcher类,继承 org.hamcrest.BaseMatcher 类,并声明入参类型的泛型。
实现 matches() 方法,该方法是assertThat 的回调方法,模板方法,实际的断言逻辑。
实现 describeTo() 方法,该方法控制断言失败时的文本提示信息。
自行创建静态工厂方法,用于创建Matcher类,类似于 equalTo() 这种方法。
代码示例如下:

public class StringEqualTo<T extends String> extends BaseMatcher<T> {

    private String value;

    public StringEqualTo(String value) {
        this.value = value;
    }

    // 这个是assertThat 的回调方法,模板方法,实际的断言逻辑
    @Override
    public boolean matches(Object item) {
        return StringUtils.equals(value, (String)item);
    }

    // @Factory 注解标识该静态方法 是 创建 matcher 的工程方法,只是标识,没有别的作用
    @Factory
    public static <T extends String> StringEqualTo<T> stringEqualTo(T t) {
        return new StringEqualTo<>(t);
    }

    // 该方法控制断言失败时的文本提示信息
    @Override
    public void describeTo(Description description) {
        description.appendText("字符串不匹配");
    }

}

使用方法跟其默认实现的一样,示例如下:

@Test
public void testMatcher() {
    String testValue = "hello";
    assertThat(testValue, StringEqualTo.stringEqualTo("hello"));
}

最后给大家分享一下我的一些学习资料:
mockito.when,软件测试,自动化测试,功能测试
以上内容,对于软件测试的朋友来说应该是最全面最完整的备战仓库了,为了更好地整理每个模块,我也参考了很多网上的优质博文和项目,力求不漏掉每一个知识点,很多朋友靠着这些内容进行复习,拿到了BATJ等大厂的offer,这个仓库也已经帮助了很多的软件测试的学习者,希望也能帮助到你。

关注下方我的微信公众号免费领取!↓ ↓ ↓ ↓ ↓文章来源地址https://www.toymoban.com/news/detail-851950.html

到了这里,关于【单元测试】Mockito使用详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用 Mockito 对 Flutter 代码进行单元测试

    单元测试验证单个方法或类是否按预期工作。它还通过在进行新更改时确认现有逻辑是否仍然有效来提高可维护性。 通常,单元测试很容易编写,但可以在测试环境中运行。400默认情况下,这会在进行网络调用或 HTTP 请求时产生带有状态代码的空响应。为了解决这个问题,我

    2024年02月08日
    浏览(31)
  • 使用Mockito针对多线程场景编写单元测试

    Mockito是一个Java的Mocking框架,主要用于编写单元测试。针对多线程编程的单元测试,可以使用Mockito的一些特性和技巧来完成。  Mockito支持异步调用技术,可以使用Mockito.when().thenReturnAsync()方法来模拟异步调用的返回值。这样可以模拟多线程编程的情况。 例如,假设有一个异

    2024年02月09日
    浏览(33)
  • 单元测试利器——手把手教你使用Mockito

    作者:京东零售 秦浩然 从你成为开发人员的那一天起,写单元测试终究是你逃不开的宿命!那开发人员为什么不喜欢写单元测试呢?究其原因,无外乎是依赖。依赖其他的服务、依赖运行的环境、等等,各种依赖都成为了我们写单元测试的绊脚石。那现在有个单元测试利器

    2024年02月08日
    浏览(53)
  • SpringBoot单元测试--Mockito+Junit5框架使用

    作为程序员为了提前发现代码bug,优化代码; 通常我们写完某个功能模块代码后都需要写单元测试对代码块进行测试(特别是敏捷开发中);Java项目最常用的单元测试框架即为Junit(目前最新版本为Junit5),SpringBoot本身也整合了该框架。在写单元测试时代码块中的调到第三方接口方

    2024年02月02日
    浏览(37)
  • 在Spring Boot环境中使用Mockito进行单元测试

    Mockito是一个流行的Java mocking框架,它允许开发者以简单直观的方式创建和使用模拟对象(mocks)。Mockito特别适用于在Spring Boot环境中进行单元测试,因为它能够轻松模拟Spring应用中的服务、存储库、客户端和其他组件。通过使用Mockito,开发者可以模拟外部依赖,从而使单元测

    2024年03月23日
    浏览(36)
  • Spring Boot使用JUnit和Mockito进行Service层单元测试

      在平时的开发当中,一个项目往往包含了大量的方法,可能有成千上万个。如何去保证这些方法产生的结果是我们想要的呢?那么在SpringBoot项目中我们是如何对Service层的方法进行测试的呢?   单元测试是一种软件测试方法,用于测试代码的最小可测试单元。在Java开发

    2024年02月03日
    浏览(44)
  • spring boot 单元测试JUnit5使用Mockito模拟Mock数据调用

    spring boot 单元测试JUnit5使用Mockito模拟Mock数据调用 好大一批新用法,大家静下心来好好看看吧 1. spring boot 使用 Mockito.when().thenReturn()模拟返回值 Mockito 是一种 Java mock 框架,他主要就是用来做 mock 测试的,他可以模拟任何 Spring 管理的 bean、模拟方法的返回值、模拟抛出异常…

    2024年02月15日
    浏览(41)
  • 单元测试(mock) 错误org.mockito.Mockito.framework()Lorg/mockito/MockitoFramework;解析

      如果您觉得有用的话,记得给 博主点个赞,评论,收藏一键三连啊 ,写作不易啊^ _ ^。   而且听说 点赞的人每天的运气都不会太差 ,实在白嫖的话,那欢迎常来啊!!! 01 准备 01::01 安装maven dependency helper插件和maven helper插件 用来分析你的依赖 打开idea 选择 File - Settin

    2023年04月08日
    浏览(29)
  • 单元测试-mockito

    在单元测试中,对于一个类中的方法,常常需要依赖其他类的方法、操作数据dto等对象实例。 方法mock:依赖的方法又可能依赖其他方法,呈现级联的树状结构。 问题:在一些情况下,这个依赖树会很深,其中依赖的一个子方法一旦修改出现问题,如果引起大量的单测不可用

    2024年04月11日
    浏览(59)
  • 单元测试与Mockito

    系列文章目录和关于我 最近在新公司第一次上手写代码,写了一个不是很难的业务逻辑代码,但是在我写单元测试的时候,发现自己对单元测试的理解的就是一坨,整个过程写得慢,还写得臭。造成这种局面我认为是因为: 对Mockito api是不是很熟悉 没有自己单元测试方法论

    2023年04月22日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包