Java|spring Mock入门

/ 测试Java / 没有评论 / 1888浏览

背景

Java中目前主要的Mock测试工具有Mockito,JMock,EasyMock等等,很多Java Mock库如EasyMock或JMock都是expect-run-verify(期望-运行-测试)的方式,而Mockito则更简单:在执行后的互动中提问。使用Mockito主要记住,在执行前stub,而后在交互中验证即可。

mock测试

就是在测试过程中,对于某些不容易构造或者 不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。这个虚拟的对象就是mock对象。mock对象就是真实对象在调试期间的代替品。

Requirements(需求)

No Dependency:每一个团队都希望自己开发的模块不依赖任何其它的外界条件,沟通成本仅限于双方接口定义。

Why(为什么要使用它?)

Principle

通过定义基于方法的模拟调用规则模拟任何代码的调用过程替代真实代码执行!

How(如何使用?)

场景

模拟RPC服务:目前存在很多应用通过RPC服务调用获取数据,应用前端的展现严重依赖后端服务的稳定性,在测试阶段可以选择通过模拟的方式直接模拟后端服务。

一、在项目中使用Maven引用mock

<dependency> 
  <groupId>org.mockito</groupId> 
  <artifactId>mockito-all</artifactId> 
  <version>1.8.5</version>
  <scope>test</scope> 
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>2.7.13</version>
</dependency>
<dependency>  
  <groupId>org.springframework</groupId>  
  <artifactId>spring-test</artifactId>  
</dependency> 

二、例子

import static org.mockito.Mockito.*;
//mock creation
List mockedList = mock(List.class);
//using mock object
mockedList.add("one");
mockedList.clear();

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

一旦创建 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, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");

-(2)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));

-(3)迭代器stubbing,例子

when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException())
  .thenReturn("foo")

  when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException())
  .thenReturn("foo")

参数匹配器允许灵活的验证或stubbing

//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt()))
  .thenReturn("element");

//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
when(mockedList.contains(argThat(isValid())))
  .thenReturn("element");

//following prints "element"
System.out.println(mockedList.get(999));

//you can also verify using an argument matcher
verify(mockedList).get(anyInt());

Note:创建mock,使用@Mock注解

public class ArticleManagerTest {
 @Mock private ArticleCalculator calculator;
 @Mock private ArticleDatabase database;
 @Mock private UserProvider userProvider;
 private ArticleManager manager;

在基础类或者测试 runner 里面:

MockitoAnnotations.initMocks(testClass);

例子如下:

public class ArticleManagerTest extends SampleBaseTestCase {

  @Mock private ArticleCalculator calculator;
  @Mock(name = "database") private ArticleDatabase dbMock
    @Spy private UserProvider userProvider = new ConsumerUserProvider();
  @InjectMocks private ArticleManager manager;
  @Test public void shouldDoSomething() {
    manager.initiateArticle();
    verify(database).addListener(any(ArticleListener.class));
  }
}

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

2)  利用spring的mock类进行单元测试

MockHttpServletRequest request= new MockHttpServletRequest("POST","/index.do");   
    request.addParameter("username","name");   
    request.addParameter("password","word");