mockito开始使用mockito


备注

Mockito是一个java Mocking框架,旨在通过使用它的简单API提供编写清晰可读单元测试的能力。它与其他模拟框架不同,它保留了大多数其他框架使用的expect-run-verify模式。

相反,它只知道模拟(非最终)类和接口的一种方法,并允许基于灵活的参数匹配器验证和存根。

当前版本1.10.19最好使用maven获得

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
</dependency>

或者是gradle

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }

版本2仍处于测试阶段。

版本

Maven Central 发行说明发布日期
2.1.0 的Mockito核心 变化 2016年10月4日
19年1月10日 的Mockito核心 变化 2014-12-31

向mock对象添加行为

Mockito.when(mock.returnSomething()).thenReturn("my val");

mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "my val" again
mock.returnSomething(); // returns "my val" again and again and again...
 

如果你想在第二次调用时获得不同的值,你可以在thenReturn方法中添加想要的返回参数:

Mockito.when(mock.returnSomething()).thenReturn("my val", "other val");

mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "other val"
mock.returnSomething(); // returns "other val" again
 

如果您将调用方法而不向mock添加行为,则它将返回null:

barMock.mock.returnSomethingElse(); // returns null
 

如果mocked方法有参数,你也应该声明值:

Mockito.when(mock.returnSomething("param 1")).thenReturn("my val 1");
Mockito.when(mock.returnSomething("param 2")).thenReturn("my val 2");

mock.returnSomething("param 1"); // returns "my val 1"
mock.returnSomething("param 2"); // returns "my val 2"
mock.returnSomething("param 3"); // returns null
 

如果您不关心param值,可以使用Matchers.any():

Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");

mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"
 

要抛出异常使用thenThrow方法:

Mockito.when(mock.returnSomething()).thenThrow(new Exception());

mock.returnSomething(); // throws Exception
 

检查传递给mock的参数

让我们假设我们有这个类,我们想测试doSmth 方法。在这种情况下,我们想看看参数“val”是否传递给foo 。对象foo 被嘲笑。

public class Bar {

    private final Foo foo;

    public Bar(final Foo foo) {
        this.foo = foo;
    }

    public void doSmth() {
        foo.bla("val");
    }
}
 

我们可以使用ArgumentCaptor 实现这一目标:

@Mock
private Foo fooMock;

@InjectMocks
private Bar underTest;

@Captor
private ArgumentCaptor<String> stringCaptor;

@Test
public void should_test_smth() {
    underTest.doSmth();

    Mockito.verify(fooMock).bla(stringCaptor.capture());

    assertThat(stringCaptor.getValue(), is("val"));
}
 

创建由Mockito模拟的对象

创建由Mockito模拟的对象有两种方法:

  • 通过注释
  • 通过模拟功能

通过注释:

使用JUnit测试运行器:

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
    @Mock
    private Bar barMock;

    // ...
}
 

你也可以使用Mockito的JUnit @Rule ,它提供与MockitoJUnitRunner 相同的功能,但不需要@RunWith 测试运行器:

public class FooTest {
    @Rule
    public MockitoRule mockito = MockitoJUnit.rule();        

    @Mock
    private Bar barMock;

    // ...
}
 

如果你不能使用@RunWith@Rule 注释,你也可以“按手”初始化:

public class FooTest {
    @Mock
    private Bar barMock;

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

    // ...
}
 

通过模拟功能:

public class FooTest {
    private Bar barMock = Mockito.mock(Bar.class);

    // ...
}
 

由于类型擦除,您不能像上面那样模拟泛型类。您必须模拟基类并显式转换为正确的泛型类型:

public class FooTest {
    private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);

    // ...
}
 

安装和设置

安装

安装Mockito的首选方法是使用选择的构建系统声明对mockito-core 的依赖。截至2016年7月22日,最新的非beta版本为1.10.19,但已鼓励2.x迁移到

Maven的

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
 

摇篮

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
 

除了Mockito本身之外,还有包含Hamcrest和Objenesis的mockito-all 。它通过Maven主要为蚂蚁用户提供,但Mockito 2.x已经停止发布。


进口

最的设施的Mockito是静态方法org.mockito.Mockito 。因此,Mockito可以通过这种方式静态导入到类中:

import static org.mockito.Mockito.*;
 

文档入口点位于此类的javadoc中。

在对象上模拟一些方法

只需使用mock的spy() 来模拟对象的某些方法。

例如,假设方法类需要一些Web服务才能工作。

public class UserManager {

    List<User> users;        

    public UserManager() {
        user = new LinkedLisk<User>();
    }
    
    public void addUser(User user) {
        if (isValid(user)) {
            user.add(user);
        } else {
            throw new NotValidUserException();
        }
    }
    
    protected boolean isValid(User user) {
        //some online web service to check if user is valid
    }
    
    public int numberOfUsers() {
        return users.size();
    }
}
 

必须测试addUser 方法才能为UserManager 进行有用的测试。但是,这里找到依赖关系, isValid 需要一个未包含在我们的代码中的外部Web服务。然后,应该中和这种外部依赖。

在这种情况下,如果只模拟isValid 您将能够测试其余的UserManager 方法。

@Test
public void testAddUser() {
    User user = mock(User.class);
    UserManager  manager = spy(new UserManager());
    
    //it forces to manager.isValid to return true
    doReturn(true).when(manager).isValid(anyObject());
    
    manager.addUser(user);
    assertTrue(manager.numberOfUsers(), 1);
} 
 

您可以轻松检查user 无效的方案。

@Test(expectedExceptions = NotValidUserException.class)
public void testNotValidAddUser() {
    User user = mock(User.class);
    UserManager  manager = spy(new UserManager());
    
    //it forces to manager.isValid to return false
    doReturn(false).when(manager).isValid(anyObject());
    
    manager.addUser(user);
} 
 

简单的最小Mockito测试

此示例显示了使用模拟ArrayList 的最小Mockito测试:

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest
{
    @Mock
    ArrayList<String> listMock;

    @Test
    public void testAppend() {
        // configure the mock to return "foobar" whenever "get()" 
        // is called on "listMock" with an int value as parameter
        doReturn("foobar").when(listMock).get(anyInt());            
        String result = listMock.get(0);
        
        assertEquals("foobar", result);
    }
}
 

使用Mockito进行简单的单元测试

我们要测试的课程是:

public class Service {

    private Collaborator collaborator;

    public Service(Collaborator collaborator) {
        this.collaborator = collaborator;
    }
    
    public String performService(String input) {
        return collaborator.transformString(input);
    }
}
 

它的合作者是:

public class Collaborator {

    public String transformString(String input) {
        return doStuff();
    }

    private String doStuff() {
        // This method may be full of bugs
        . . .
        return someString;
    }

}
 

在我们的测试中,我们希望打破Collaborator 及其bug的依赖性,因此我们将模拟Collaborator

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.junit.Test;

public class ServiceTest {
    @Test
    public void testPerformService() throws Exception {
        // Configure mock
        Collaborator collaboratorMock = mock(Collaborator.class);
        doReturn("output").when(collaboratorMock).transformString("input");

        // Perform the test
        Service service = new Service(collaboratorMock);
        String actual = service.performService("input");
        
        // Junit asserts
        String expected = "output";
        assertEquals(expected, actual);
    }  
}
 

Stubbing void方法

void 方法可以使用doThrow()doAnswer()doNothing()doCallRealMethod()方法系列进行存根

Runnable mock = mock(Runnable.class);

doThrow(new UnsupportedOperationException()).when(mock).run();

mock.run(); // throws the UnsupportedOperationException
 

请注意, void 方法不能使用when(..) 导致编译器不喜欢void 方法作为参数。

使用Mockito注释

我们要测试的课程是:

public class Service{

    private Collaborator collaborator;

    public Service(Collaborator collaborator){
        this.collaborator = collaborator;
    }
    
    
    public String performService(String input){
        return collaborator.transformString(input);
    }
}
 

它的合作者是:

public class Collaborator {

    public String transformString(String input){
        return doStuff();
    }

    private String doStuff()
    {
        // This method may be full of bugs
        . . .
        return someString;
    }

}
 

在我们的测试中,我们希望打破Collaborator 及其错误的依赖性,因此我们将模拟Collaborator 。使用@Mock 注释是为每个测试创建不同模拟实例的便捷方法:

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {

    @Mock
    private Collaborator collaboratorMock;

    @InjectMocks
    private Service service;
    
    @Test
    public void testPerformService() throws Exception {
        // Configure mock
        doReturn("output").when(collaboratorMock).transformString("input");            

        // Perform the test
        String actual = service.performService("input");
        
        // Junit asserts
        String expected = "output";
        assertEquals(expected, actual);
    }
    
    
    @Test(expected=Exception.class)
    public void testPerformServiceShouldFail() throws Exception {
        // Configure mock
        doThrow(new Exception()).when(collaboratorMock).transformString("input");

        // Perform the test
        service.performService("input");
    }
}
 

Mockito将尝试按以下顺序解决依赖注入:

  1. 基于构造函数的注入 - 使用大多数参数将模拟注入到构造函数中(如果找不到某些参数,则传递空值)。如果通过构造函数成功创建了对象,则不会应用其他策略。
  2. 基于定位器的注射 - 模拟按类型注入。如果存在多个相同类型的属性,则将匹配属性名称和模拟名称。
  3. 直接进样 - 与基于setter的进样相同。

请注意,如果上述任何策略失败,则不会报告失败。

有关@InjectMocks 最新版本中此机制的更多详细信息,请参阅最新的@InjectMocks。

验证对模拟对象的方法调用

要检查是否在Mockito.verify 对象上调用了方法,可以使用Mockito.verify 方法:

Mockito.verify(someMock).bla();
 

在这个例子中,我们断言在someMock 模拟对象上调用方法bla

您还可以检查是否使用某些参数调用了方法:

Mockito.verify(someMock).bla("param 1");
 

如果要检查是否调用方法,可以传递其他VerificationMode 参数来verify

Mockito.verify(someMock, Mockito.times(0)).bla();
 

如果你想检查这个方法被多次调用(在这种情况下我们检查方法bla 被调用了23次),这也有效:

Mockito.verify(someMock, Mockito.times(23)).bla();
 

这些是VerificationMode 参数的更多示例,可以更多地控制应该调用方法的次数:

Mockito.verify(someMock, Mockito.never()).bla(); // same as Mockito.times(0)

Mockito.verify(someMock, Mockito.atLeast(3)).bla(); // min 3 calls

Mockito.verify(someMock, Mockito.atLeastOnce()).bla(); // same as Mockito.atLeast(1)

Mockito.verify(someMock, Mockito.atMost(3)).bla(); // max 3 calls
 

使用ArgumentCaptor验证参数

要验证在mock上调用的方法的ArgumentCaptor ,请使用ArgumentCaptor 类。这将允许您将参数提取到测试方法中并对它们执行断言。

此示例测试一个更新具有给定ID的用户名的方法。该方法加载用户,使用给定值更新name 属性并在之后保存。测试想要验证传递给save 方法的参数是具有正确ID和名称的User 对象。

// This is mocked in the test
interface UserDao {
    void save(User user);
}

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    @Mock
    UserDao userDao;

    @Test
    public void testSetNameForUser() {
        UserService serviceUnderTest = new UserService(userDao);
        
        serviceUnderTest.setNameForUser(1L, "John");

        ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class);
        
        verify(userDao).save(userArgumentCaptor.capture());
        User savedUser = userArgumentCaptor.getValue();
        assertTrue(savedUser.getId() == 1);
        assertTrue(savedUser.getName().equals("John"));
    }
}
 

使用ArgumentMatcher验证参数

Mockito提供了一个Matcher<T> 接口以及一个抽象的ArgumentMatcher<T> 类来验证参数。它使用与ArgumentCaptor 相同的用例的不同方法。此外,ArgumentMatcher也可用于模拟。两个用例都使用Mockito.argThat() 方法,该方法提供了一个合理可读的测试代码。

verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
       
    @Override
    public boolean matches(Object o) {
        return o instanceof String && !((String)o).isEmpty();
    }

});        
 

从ArgumentMatcher的JavaDocs:

警告:使用复杂的参数匹配是合理的,尤其是自定义参数匹配器,因为它可以使测试的可读性降低。有时最好为传递给模拟的参数实现equals()(Mockito自然地使用equals()进行参数匹配)。这可以使测试更清洁。