diguage / www.diguage.com

"地瓜哥"博客网
https://www.diguage.com/
Apache License 2.0
1 stars 2 forks source link

Mockito User Manual #1

Open diguage opened 5 years ago

diguage commented 5 years ago

Mockito (Mockito 3.1.0 API)

Mockito (Mockito 3.6.0 API)

重点关注问题

  1. Spy 与 Mock 的区别 SpringBoot 单元测试--Mockito之spy
  2. 如何测试 DB,单元测试:(一)在已完成的 spring + mybatis 项目中加入 dbunit 完成 dao 层自动化测试
  3. Spring Bean 依赖, 单元测试中简单使用Mockito解决Spring Bean依赖树问题
  4. Mockito的使用及实现原理 | Alben's home
  5. 反模式的经典 - Mockito设计解析-InfoQ
diguage commented 5 years ago

1. Let's verify some behaviour!

 //mock creation
 List mockedList = mock(List.class);

 //using mock object
 mockedList.add("one");
 mockedList.clear();

 //verification
 verify(mockedList).add("one");
 verify(mockedList).clear();

Once created, a mock will remember all interactions. Then you can selectively verify whatever interactions you are interested in.

D瓜哥注:这里可以校验被调用的次数,已经方法传入的参数。

ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);

verify(mockedList).add(argumentCaptor.capture());

String one = argumentCaptor.getValue();
diguage commented 5 years ago

2. How about some stubbing?

 //You can mock concrete classes, not just interfaces
 LinkedList mockedList = mock(LinkedList.class);

 //stubbing
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());

 //following prints "first"
 System.out.println(mockedList.get(0));

 //following throws runtime exception
 System.out.println(mockedList.get(1));

 //following prints "null" because get(999) was not stubbed
 System.out.println(mockedList.get(999));

 //Although it is possible to verify a stubbed invocation, usually it's just redundant
 //If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
 //If your code doesn't care what get(0) returns, then it should not be stubbed.
 verify(mockedList).get(0); // 不明白这是啥意思。

D瓜哥注:

  1. 也可以 mock 接口: mock(List.class)
  2. 默认返回初始化“空值”
  3. Stubbing can be overridden。我的理解是,打桩可以被覆盖掉。
  4. 最后一次打桩最重要。不过,有时针对同一个方法打桩的顺序也需要特别关注。 -- 这个情况还需要再仔细思考一下。
  5. 没有用到的方法,不需要打桩,打桩会报错。
diguage commented 5 years ago

3. Argument matchers

List mockedList = mock(List.class);
when(mockedList.get(anyInt())).thenReturn("element");

//        when(mockedList.contains(argThat(any()))).thenReturn("element");
System.out.println(mockedList.get(999));

verify(mockedList).get(anyInt());

verify(mockedList).add(argThat((ArgumentMatcher<String>) argument -> argument.length() > 5));

D瓜哥注:

  1. Argument matchers allow flexible verification or stubbing.
  2. 可以实现 ArgumentMatcher 来自定义参数匹配器
  3. 参数匹配器的场景还需要多想想。
  4. If you are using argument matchers, all arguments have to be provided by matchers. -- 不能有的是参数匹配器,有的是变量。
  5. Matcher methods like anyObject(), eq() do not return matchers.
diguage commented 5 years ago

4. Verifying exact number of invocations / at least x / never

List mockedList = mock(List.class);
//using mock
mockedList.add("once");

mockedList.add("twice");
mockedList.add("twice");

mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");

//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");

//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");

//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");

//verification using atLeast()/atMost()
verify(mockedList, atMost(1)).add("once");
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");

times(1) is the default.

可以精确统计每个参数的调用次数。这个厉害了!

diguage commented 5 years ago

5. Stubbing void methods with exceptions

List mockedList = mock(List.class);
doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();
diguage commented 5 years ago

6. Verification in order

// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");

InOrder inOrder = inOrder(singleMock);

//following will make sure that add is first called with "was added first", then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");

// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);

//using mocks
firstMock.add("was called first");
secondMock.add("was called second");

//create inOrder object passing any mocks that need to be verified in order
InOrder multiInOrder = inOrder(firstMock, secondMock);

//following will make sure that firstMock was called before secondMock
multiInOrder.verify(firstMock).add("was called first");
multiInOrder.verify(secondMock).add("was called second");

// Oh, and A + B can be mixed together at will

D瓜哥注:

  1. 竟然可以验证调用顺序。
  2. Verification in order is flexible.
diguage commented 5 years ago

7. Making sure interaction(s) never happened on mock

List mockOne = mock(List.class);
List mockTwo = mock(List.class);
List mockThree = mock(List.class);

//using mocks - only mockOne is interacted
mockOne.add("one");

//ordinary verification
verify(mockOne).add("one");

//verify that method was never called on a mock
verify(mockOne, never()).add("two");

verifyZeroInteractions(mockTwo, mockThree);
  1. 验证是根据参数来做的验证。
diguage commented 5 years ago

8. Finding redundant invocations

List mockedList = mock(List.class);

//using mocks
mockedList.add("one");
//        mockedList.add("two");

verify(mockedList).add("one");

//following verification will fail
verifyNoMoreInteractions(mockedList);
  1. 如果有多余的交互就报错
  2. 如果有 verify(),则不算多余
  3. 不要滥用 verifyNoMoreInteractions()
diguage commented 5 years ago

9. Shorthand for mocks creation - @Mock annotation

   public class ArticleManagerTest {

       @Mock private ArticleCalculator calculator;
       @Mock private ArticleDatabase database;
       @Mock private UserProvider userProvider;

       private ArticleManager manager;

// Important! This needs to be somewhere in the base class or a test runner:
MockitoAnnotations.initMocks(testClass);
  1. 注意:使用注解,要 MockitoAnnotations.initMocks(this); 来初始化一下。
  2. 也可以使用 MockitoJUnitRunner 来运行,则不需要 MockitoAnnotations.initMocks(this);
diguage commented 5 years ago

10. Stubbing consecutive calls (iterator-style stubbing)

SomeObject mock = mock(SomeObject.class);
when(mock.someMethod("some arg"))
        .thenThrow(new RuntimeException())
        .thenReturn("foo");

//First call: throws runtime exception:
assertThatThrownBy(() -> mock.someMethod("some arg"))
        .isInstanceOf(RuntimeException.class);

//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));

//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));

when(mock.someMethod("some arg"))
        .thenReturn("one", "two", "three");

System.out.println(mock.someMethod("some arg"));
System.out.println(mock.someMethod("some arg"));
System.out.println(mock.someMethod("some arg"));
System.out.println(mock.someMethod("some arg"));

//All mock.someMethod("some arg") calls will return "two"
when(mock.someMethod("some arg"))
        .thenReturn("one");
when(mock.someMethod("some arg"))
        .thenReturn("two");
  1. 可以在不同的调用次数,返回不同的值。
  2. multiple stubbing with the same matchers or arguments is used, then each stubbing will override the previous one.
diguage commented 5 years ago

11. Stubbing with callbacks

Allows stubbing with generic Answer interface.

when(mock.someMethod(anyString())).thenAnswer(
        new Answer<String>() {
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                Object mock = invocation.getMock();
                return "called with arguments: " + Arrays.toString(args);
            }

        }
);
//Following prints "called with arguments: [foo]"
System.out.println(mock.someMethod("foo"));

可以根据参数来生成不同的返回值。

diguage commented 5 years ago

12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() family of methods

对于没有返回值的方法,不要需要 stub,则需要通过不同的方式:

List mockList = mock(List.class);
doThrow(new RuntimeException()).when(mockList).clear();
mockList.clear();

类似的方法还有:

这里需要特别注意一点:如果是 spy() 某个实例,使用 when(spy.get(0)).thenReturn("foo"); 则会报错。则就可以使用这种方式来实现根据需求返回不同的结果。例如:

List<String> list = new LinkedList<>();
// list.add("Hello");
List<String> spy = spy(list);
//Impossible: real method is called so spy.get(0) throws 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);
System.out.println(spy.get(0));

Spying should be sporadic and overriding exception-stubbing is very rare. Not to mention that in general overridding stubbing is a potential code smell that points out too much stubbing.

diguage commented 5 years ago

13. Spying on real objects

You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).

Real spies should be used carefully and occasionally.

List<String> list = new LinkedList<>();
List<String> spy = spy(list);

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls *real* methods
spy.add("one");
spy.add("two");

//prints "one" - the first element of a list
System.out.println(spy.get(0));

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");
  1. 注意区分 spy 对象和真实的实例对象。
  2. 不能模拟 final 方法。
diguage commented 5 years ago

14. Changing default return values of unstubbed invocations (Since 1.7)

SomeClass mock = mock(SomeClass.class, RETURNS_SMART_NULLS);
System.out.println(mock.someMethod("123"));
SomeClass object = mock.object();
System.out.println(object);
System.out.println(mock.intMethod(119));

SomeClass deepStubMock = mock(SomeClass.class, RETURNS_DEEP_STUBS);
System.out.println(deepStubMock.someMethod("123"));
SomeClass deepObject = deepStubMock.object();
System.out.println(deepObject);
System.out.println(deepStubMock.intMethod(119));

SomeClass mockTwo = mock(SomeClass.class, new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return "default answer";
    }
});
System.out.println(mockTwo.someMethod("test14"));
System.out.println(mockTwo.method2("test14"));
System.out.println(mockTwo.intMethod(2));

可以使用这个方式定义类中所有方法的返回值。

这里需要注意:

  1. Unstubbed methods often return null.
  2. 如果返回值类型不匹配,则会报错。
  3. 默认返回值只会针对没有打桩的方法。
  4. RETURNS_SMART_NULLS 会自动根据方法返回值类型来返回匹配的类型。

值得关注的对象

注意: deep stubsmocks 啥区别?

diguage commented 5 years ago

15. Capturing arguments for further assertions (Since 1.8.0)

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
SomeClass mock = mock(SomeClass.class);
verify(mock).string(argument.capture());
assertThat("John").isEqualTo(argument.getValue());

可以捕获传入的参数。然后进行校验。

diguage commented 5 years ago

16. Real partial mocks (Since 1.8.0)

Object oriented programming tackles complexity by dividing the complexity into separate, specific, SRPy objects. How does partial mock fit into this paradigm?

Partial mock usually means that the complexity has been moved to a different method on the same object.

//you can create partial mock with spy() method:
List list = spy(new LinkedList());

//you can enable partial mock capabilities selectively on mocks:
Foo mock = mock(Foo.class);
//Be sure the real implementation is 'safe'.
//If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
when(mock.someMethod()).thenCallRealMethod(); // 竟然可以这样调用真实方法。
diguage commented 5 years ago

17. Resetting mocks (Since 1.8.0)

Please keep us small & focused on a single behavior.

List mock = mock(List.class);
when(mock.size()).thenReturn(10);
mock.add(1);

reset(mock);
//at this point the mock forgot any interactions & stubbing

The only reason we added reset() method is to make it possible to work with container-injected mocks.

注意:一般情况下,不要使用 reset()。使用 reset() 是一个坏味道。

diguage commented 5 years ago

18. Troubleshooting & validating framework usage (Since 1.8.0)

Mockito 不能 mock 静态方法;不能 mock hashCode 和 equals 方法;不能 mock 构造函数;

diguage commented 5 years ago

19. Aliases for behavior driven development (Since 1.8.0)

Behavior Driven Development style of writing tests uses //given //when //then comments as fundamental parts of your test methods.

BDDMockito class introduces an alias so that you stub method calls with BDDMockito.given(Object) method.

 import static org.mockito.BDDMockito.*;

Seller seller = mock(Seller.class);
Shop shop = new Shop(seller);

public void shouldBuyBread() throws Exception {
    //given
    given(seller.askForBread()).willReturn(new Bread());

    //when
    Goods goods = shop.buyBread();

    //then
    assertThat(goods, containBread());
}

TODO: 这里例子不是很明白,还需要再完善一下。

diguage commented 5 years ago

20. Serializable mocks (Since 1.8.1)

List serializableMock = mock(List.class, withSettings().serializable());

// 对一个真实对象的Spy进行序列化
List<Object> list = new ArrayList<>();
ArrayList spy = mock(ArrayList.class,
        withSettings()
                .spiedInstance(list)
                .defaultAnswer(CALLS_REAL_METHODS)
                .serializable());
diguage commented 5 years ago

21. New annotations: @Captor, @Spy, @InjectMocks (Since 1.8.3)

@InjectMocks can also be used in combination with the @Spy annotation。注意:这样会创建一个 partial mock。

所有的注解都要被 MockitoAnnotations.initMocks(Object) 来处理。

diguage commented 5 years ago

22. Verification with timeout (Since 1.8.5)

SomeObject mock = mock(SomeObject.class);

mock.someMethod();

//passes when someMethod() is called no later than within 100 ms
//exits immediately when verification is satisfied (e.g. may not wait full 100 ms)
verify(mock, timeout(100)).someMethod();
//above is an alias to:
verify(mock, timeout(100).times(1)).someMethod();

//passes as soon as someMethod() has been called 2 times under 100 ms
verify(mock, timeout(100).times(2)).someMethod();

//equivalent: this also passes as soon as someMethod() has been called 2 times under 100 ms
verify(mock, timeout(100).atLeast(2)).someMethod();

Mockito 还可以校验超时。

diguage commented 5 years ago

23. Automatic instantiation of @Spies, @InjectMocks and constructor injection goodness (Since 1.9.0)

Mockito 会自动实例化 @Spy@InjectMocks 变量。

diguage commented 5 years ago

24. One-liner stubs (Since 1.9.0)

Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();

Mockito 可以在一行代码内即创建 Mock 又 Stub。

diguage commented 5 years ago

25. Verification ignoring stubs (Since 1.9.0)

verifyNoMoreInteractions() 这是何意?看的不是很明白。

diguage commented 5 years ago

26. Mocking details (Improved in 2.2.x)

//To identify whether a particular object is a mock or a spy:
Mockito.mockingDetails(someObject).isMock();
Mockito.mockingDetails(someObject).isSpy();

//Getting details like type to mock or default answer:
MockingDetails details = mockingDetails(mock);
details.getMockCreationSettings().getTypeToMock();
details.getMockCreationSettings().getDefaultAnswer();

//Getting invocations and stubbings of the mock:
MockingDetails details = mockingDetails(mock);
details.getInvocations();
details.getStubbings();

//Printing all interactions (including stubbing, unused stubs)
System.out.println(mockingDetails(mock).printInvocations());

Mockito 提供了 Mock 对象的相关信息。

diguage commented 5 years ago

27. Delegate calls to real instance (Since 1.9.5)

final class DontYouDareToMockMe implements List { ... }

DontYouDareToMockMe awesomeList = new DontYouDareToMockMe();

List mock = mock(List.class, delegatesTo(awesomeList));

可以通过这种方式把调用传递给真是对象的对应方法。

这个方式可以用于 final 类的测试。

diguage commented 5 years ago

28. MockMaker API (Since 1.9.5)

By default, Mockito uses Byte Buddy to create dynamic proxies.

Mockito 还可以用于 Android 的测试,请看 linkedin/dexmaker

diguage commented 5 years ago

29. BDD style verification (Since 1.10.0)

 given(dog.bark()).willReturn(2);

 // when
 ...

 then(person).should(times(2)).ride(bike);

Mockito 支持 BDD 风格的校验。请引入 org.mockito.BDDMockito

diguage commented 5 years ago

30. Spying or mocking abstract classes (Since 1.10.12, further enhanced in 2.7.13 and 2.7.14)

//convenience API, new overloaded spy() method:
SomeAbstract spy = spy(SomeAbstract.class);

//Mocking abstract methods, spying default methods of an interface (only available since 2.7.13)
Function function = spy(Function.class);

//Robust API, via settings builder:
OtherAbstract spy = mock(OtherAbstract.class, withSettings()
        .useConstructor().defaultAnswer(CALLS_REAL_METHODS));

//Mocking an abstract class with constructor arguments (only available since 2.7.14)
SomeAbstract spy = mock(SomeAbstract.class, withSettings()
        .useConstructor("arg1", 123).defaultAnswer(CALLS_REAL_METHODS));

//Mocking a non-static inner abstract class:
InnerAbstract spy = mock(InnerAbstract.class, withSettings()
        .useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));

Mockito 竟然可以为抽象类上创建 Spy 。

diguage commented 5 years ago

31. Mockito mocks can be serialized / deserialized across classloaders (Since 1.10.0)

// use regular serialization
mock(Book.class, withSettings().serializable());

// use serialization across classloaders
mock(Book.class, withSettings().serializable(ACROSS_CLASSLOADERS));

不知有何用意?

diguage commented 5 years ago

32. Better generic support with deep stubs (Since 1.10.0)

class Line{}
interface Lines extends List<Line>{}

Lines lines = mock(Lines.class, RETURNS_DEEP_STUBS);

// Now Mockito understand this is not an Object but a Line
Line line = lines.iterator().next();

Mockito 对泛型的支持很好。

diguage commented 5 years ago

33. Mockito JUnit rule (Since 1.10.17)

没搞清楚 MockitoRule 有什么用?而且在 JUnit 5 中,对 rule 做了非常大的重构。

diguage commented 5 years ago

34. Switch on or off plugins (Since 1.10.15)

插件机制正在开发。请看 PluginSwitch (Mockito 3.1.0 API)

diguage commented 5 years ago

35. Custom verification failure message (Since 2.1.0)

// will print a custom message on verification failure
verify(mock, description("This will print on failure")).someMethod();

// will work with any verification mode
verify(mock, times(2).description("someMethod should be called twice")).someMethod();

可以自定义提示信息。

diguage commented 5 years ago

36. Java 8 Lambda Matcher Support (Since 2.1.0)

 // verify a list only had strings of a certain length added to it
 // note - this will only compile under Java 8
 verify(list, times(2)).add(argThat(string -> string.length() < 5));

 // Java 7 equivalent - not as neat
 verify(list, times(2)).add(argThat(new ArgumentMatcher(){
     public boolean matches(String arg) {
         return arg.length() < 5;
     }
 }));

 // more complex Java 8 example - where you can specify complex verification behaviour functionally
 verify(target, times(1)).receiveComplexObject(argThat(obj -> obj.getSubObject().get(0).equals("expected")));

 // this can also be used when defining the behaviour of a mock under different inputs
 // in this case if the input list was fewer than 3 items the mock returns null
 when(mock.someMethod(argThat(list -> list.size()<3))).thenReturn(null);

Mockito 中可以使用 Java 8 Lambda 表达式来简化代码。

diguage commented 5 years ago

37. Java 8 Custom Answer Support (Since 2.1.0)

org.mockito.stubbing.Answer 只有一个方法,可以非常方便地使用 Java 8 Lambda。不过,需要获取的参数越多,就需要越多的从 InvocationOnMock 的类型转换。

diguage commented 5 years ago

38. Meta data and generic type retention (Since 2.1.0)

 @MyAnnotation
  class Foo {
    List<String> bar() { ... }
  }

  Class<?> mockType = mock(Foo.class).getClass();
  assert mockType.isAnnotationPresent(MyAnnotation.class);
  assert mockType.getDeclaredMethod("bar").getGenericReturnType() instanceof ParameterizedType;

Mockito 会保留加持在类上的注解。

diguage commented 5 years ago

39. Mocking final types, enums and final methods (Since 2.1.0)

默认关闭。在 classpath 下创建一个 /mockito-extensions/org.mockito.plugins.MockMaker,并且包含 mock-maker-inline 来开启。

Mockito 还提供了 Jar包 mockito-inline.

Mock final 类型是不兼容 withSettings().serializable()withSettings().extraInterfaces() 的。

注意: java.* 和 native 方法不能mock。

该 mock maker 是基于 Java Agent runtime attachment 设计的。所以,需要兼容的 JVM。

更多内容可以阅读 org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker

diguage commented 5 years ago

40. Improved productivity and cleaner tests with "stricter" Mockito (Since 2.+)

diguage commented 5 years ago

41. Advanced public API for framework integrations (Since 2.10.+)

重构了API,增加了一堆接口。

diguage commented 5 years ago

42. New API for integrations: listening on verification start events (Since 2.11.+)

tackle double-proxy use case -- double-proxy 是啥玩意?

为集成 Spring Boot 提供新特性。

mock 开始时,可以发出事件通知。

List mock = mock(List.class, withSettings().verificationStartedListeners(new VerificationStartedListener() {
    @Override
    public void onVerificationStarted(VerificationStartedEvent event) {
        System.out.println(event.getMock().getClass().getName());
    }
}));
diguage commented 5 years ago

43. New API for integrations: MockitoSession is usable by testing frameworks (Since 2.15.+)

MockitoSession 不清楚改如何使用。

diguage commented 5 years ago

44. Deprecated org.mockito.plugins.InstantiatorProvider as it was leaking internal API. it was replaced by org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4)

diguage commented 5 years ago

45. New JUnit Jupiter (JUnit5+) extension

use the org.mockito:mockito-junit-jupiter artifact.

diguage commented 5 years ago

46. New Mockito.lenient() and MockSettings.lenient() methods (Since 2.20.0)

List mock = mock(List.class);
lenient().when(mock.get(0)).thenReturn("OK");

// all the stubbings on a given mock to be lenient
Foo mockFoo = mock(Foo.class, withSettings().lenient());

没搞懂啥意思。

diguage commented 5 years ago

47. New API for clearing mock state in inline mocking (Since 2.25.0)

MockitoFramework.clearInlineMocks() 清理 mock 状态以解决内存泄露问题(memory leaks)。

diguage commented 5 years ago

How to write good tests · mockito/mockito Wiki

diguage commented 5 years ago

Mockito Best Practices | Don't Make the Same Mistake Twice

diguage commented 5 years ago