一、Mock普通类的私有变量

在进行单元测试时,我们有时需要模拟一些外部依赖,例如模拟一个外部服务或者模拟一个数据库操作。这时,我们可以使用Mockito框架来创建一个普通的类的私有变量的模拟对象。

1. 添加Mockito依赖

在项目的build.gradle文件中添加以下依赖:

```groovy

dependencies {

testImplementation 'org.mockito:mockito-core:3.12.4'

}

```

2. 使用Mockito模拟私有变量

```java

import org.junit.Test;

import org.mockito.Mockito;

public class MyClassTest {

@Test

public void testPrivateVariable() {

// 创建一个MyClass的实例

MyClass myClass = new MyClass();

// 使用Mockito创建一个私有变量的模拟对象

MyClass.MyInnerClass innerClass = Mockito.mock(MyClass.MyInnerClass.class);

Mockito.when(myClass.getInnerClass()).thenReturn(innerClass);

// 调用myClass的方法,验证其内部使用的私有变量是否被正确模拟

myClass.doSomething();

}

}

```

二、对静态void方法进行Mock

有时候我们需要对一个静态void方法进行测试,但是JUnit不支持直接对静态方法进行测试。这时我们可以使用PowerMock库来实现对静态void方法的Mock。

1. 添加PowerMock依赖

在项目的build.gradle文件中添加以下依赖:

```groovy

dependencies {

testImplementation 'org.powermock:powermock-module-junit4:2.0.9'

testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'

}

```

2. 对静态void方法进行Mock

```java

import org.junit.Test;

import org.junit.runner.RunWith;

import org.powermock.api.mockito.PowerMockito;

import org.powermock.core.classloader.annotations.PrepareForTest;

import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)

@PrepareForTest({MyClass.class})

public class MyClassTest {

@Test

public void testStaticVoidMethod() throws Exception {

// 使用PowerMockito创建一个静态void方法的模拟对象

PowerMockito.mockStatic(MyClass.class);

PowerMockito.when(MyClass.staticVoidMethod()).thenReturn(null);

// 调用MyClass的静态void方法,验证其返回值是否被正确模拟为null

MyClass.staticVoidMethod();

}

}

```

Test(expected = Exception.class)

如果该测试方法没有抛出Annotation中的Exception类型(子类也可以),则测试失败。

@Test(timeout=100)

如果该测试方法耗时超过100毫秒,则测试失败,用于性能测试。

@Ignore 或者 @Ignore("太耗时")

忽略当前测试方法,一般用于测试方法还没有准备好,或者太耗时之类的。

@FixMethodOrder

定义所在的测试类中的所有测试方法都按照固定的顺序执行,可以指定3个值,分别是DEFAULT、JVM、NAME_ASCENDING(字母顺序)。

@RunWith

指定测试类的测试运行器。更多可以参考Junit官网:https://junit.org/junit4/

1. 创建测试类

接下来就可以创建测试类,除了可以手动创建测试类外,可以利用AS快捷键:将光标选中要创建测试类的类名上->按下ALT + ENTER->在弹出的弹窗中选择Create Test。这会弹出下面的弹窗,或者鼠标在类名上右键选择菜单Go to->Test,也会弹出下面的弹窗。勾选需要进行测试的方法,会自动生成一个测试类,包括@Before和@After注解。

接下来编写测试方法,首先在要测试的目标类中写几个业务方法:

```java

public class SimpleClass {

public boolean isTeenager(int age) {

if (age < 15) {

return true;

}

return false;

}

public int add(int a, int b) {

return a + b;

}

public String getNameById(int id) {

if (id == 1) {

return "小明";

} else if (id == 2){

return "小红";

}

return "";

}

}

```

然后,编写测试类:

```java

@RunWith(JUnit4.class)

public class SimpleClassTest {

private SimpleClass simpleClass;

@Before

public void setUp() throws Exception {

simpleClass = new SimpleClass();

}

...其他测试方法...

}

```

在JUnit测试框架中,我们通常使用`setUp()`和`tearDown()`方法来设置和清理测试环境。这两个方法分别在每个测试方法执行前后被调用,用于准备和清理测试所需的数据和资源。下面是重构后的代码:

```java

public void setUp() throws Exception {

simpleClass = new SimpleClass();

}

@After

public void tearDown() throws Exception {

}

@Test

public void testIsTeenager() {

Assert.assertFalse(simpleClass.isTeenager(20));

Assert.assertTrue(simpleClass.isTeenager(14));

}

@Test

public void testAdd() {

Assert.assertEquals(simpleClass.add(3, 2), 5);

Assert.assertNotEquals(simpleClass.add(3, 2), 4);

}

@Test

public void testGetNameById() {

Assert.assertEquals(simpleClass.getNameById(1), "小明");

Assert.assertEquals(simpleClass.getNameById(2), "小红");

Assert.assertEquals(simpleClass.getNameById(10), "");

}

```

在这段代码中,我们重构了原有的`isTeenager()`、`add()`和`getNameById()`测试方法,将它们的名称更改为了以`test`开头的方法名,这是JUnit的推荐命名规范。同时,我们将`setUp()`方法移除了注释符号 `//`,因为它现在是一个实际存在的方法。

在JUnit 4中,提供了一些断言方法来验证测试结果。下面是这些方法的重构后的内容:

```java

public static void assertSame(String message, Object expected, Object actual) {

if (expected != actual) {

throw new AssertionError(message);

}

}

public static void assertNotSame(Object expected, Object actual) {

if (expected == actual) {

throw new AssertionError("Expected not to be the same as actual.");

}

}

public static void assertNotSame(String message, Object expected, Object actual) {

if (expected == actual) {

throw new AssertionError(message);

}

}

public static void assertTrue(boolean condition) {

if (!condition) {

throw new AssertionError();

}

}

public static void assertTrue(String message, boolean condition) {

if (!condition) {

throw new AssertionError(message);

}

}

public static void assertFalse(boolean condition) {

if (condition) {

throw new AssertionError();

}

}

public static void assertFalse(String message, boolean condition) {

if (condition) {

throw new AssertionError(message);

}

}

public static void assertEquals(long expected, long actual) {

if (expected != actual) {

throw new AssertionError();

}

}

public static void assertEquals(String message, long expected, long actual) {

if (expected != actual) {

throw new AssertionError(message);

}

}

public static void assertEquals(Object expected, Object actual) {

if (!expected.equals(actual)) {

throw new AssertionError();

}

}

public static void assertEquals(String message, Object expected, Object actual) throws IllegalArgumentException { // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. // NOSONAR Intentionally leaving exception in method signature to satisfy findbugs checker. }

以下是根据您提供的内容重构后的文本:

```java

// 断言两个 float 类型 expect 和 actual 在 delta 偏差值下相等,delta是误差精度

void assertEquals(float expected, float actual, float delta) {

assert (Math.abs(expected - actual) <= delta);

}

// 断言两个 float 类型 expect 和 actual 在 delta 偏差值下相等,如果不相等则抛出异常携带指定的message信息

void assertEquals(String message, float expected, float actual, float delta) {

assert (Math.abs(expected - actual) <= delta);

}

// 断言两个 double 类型 expect 和 actual 在 delta 偏差值下相等

void assertEquals(double expected, double actual, double delta) {

assert (Math.abs(expected - actual) <= delta);

}

// 断言两个 double 类型 expect 和 actual 在 delta 偏差值下相等,如果不相等则抛出异常携带指定的message信息

void assertEquals(String message, double expected, double actual, double delta) {

assert (Math.abs(expected - actual) <= delta);

}

// 断言两个相同类型的数组的元素一一对应相等

void assertArrayEquals(T[] expected, T[] actual) {

int n = expected.length;

assertEquals(n, actual.length);

for (int i = 0; i < n; i++)

assertSame(expected[i], actual[i]);

}

// 断言两个相同类型的数组的元素一一对应相等,如果不相等则抛出异常携带指定的message信息

void assertArrayEquals(String message, T[] expected, T[] actual) {

int n = expected.length;

assertEquals(n, actual.length);

for (int i = 0; i < n; i++)

assertSame(message, expected[i], actual[i]);

}

// 直接让测试失败

void fail() {

AssertionError e = new AssertionError("The test failed");

e.printStackTrace();

}

// 直接让测试失败并给出message错误信息

void fail(String message) {

AssertionError e = new AssertionError(message);

e.printStackTrace();

}

```

Mockito是一个Java测试框架,它允许你模拟对象并对其进行验证。以下是一个使用Mockito的简单示例:

1. 引入Mockito依赖项到项目中:

Maven:

```xml

org.mockito

mockito-core

3.12.4

test

```

Gradle:

```groovy

testImplementation 'org.mockito:mockito-core:3.12.4'

```

2. 创建一个要模拟的类:

```java

public class MyClass {

public int add(int a, int b) {

return a + b;

}

}

```

3. 在测试类中使用Mockito模拟MyClass:

```java

import org.junit.Test;

import org.mockito.Mockito;

import static org.junit.Assert.assertEquals;

public class MyClassTest {

@Test

public void testAdd() {

MyClass myClass = Mockito.mock(MyClass.class);

int result = myClass.add(1, 2);

assertEquals(3, result);

}

}

```

Mockito是一个基于依赖注入实现的测试框架,主要用于模拟使用Android的代码测试。在传统的JUnit单元测试中,没有消除对对象的依赖,这使得测试变得困难。而Mockito能够轻松地实现对象的模拟,解决这个问题。

首先,我们需要了解Mock的概念。Mock在这里指的是“模仿”或“虚假”,即要模仿一个对象。为什么要模仿?在传统的JUnit单元测试中,没有消除对对象的依赖。例如,A对象依赖B对象的方法,在测试A对象时,我们需要构造出B对象,这增加了测试的难度,或者使得我们无法对某些类进行测试。这与单元测试的思路相违背。

另一个主要问题是本地单元测试由于运行在本地JVM环境中,无法依赖Android的API。仅靠纯Junit的测试环境很难模拟出完整的Android环境,导致无法测试Android相关的代码。而Mockito能够解决这个问题,通过Mock能够轻松地实现对象的模拟。

要使用Mockito,我们需要添加以下依赖:

```java

dependencies {

implementation 'org.mockito:mockito-core:2.19.0'

...

}

```

接下来,我们来看一下Mockito中几种Mock对象的方式:

1. 静态导入方式:

```java

import static org.mockito.Mockito.*;

```

这种方式会使代码更简洁。例如:

```java

@Test

public void testMock() {

SimpleClass mockSimple = Mockito.mock(SimpleClass.class);

assertNotNull(mockSimple);

}

```

2. 直接mock一个对象:

```java

@Test

public void testMock() {

SimpleClass mockSimple = new SimpleClass();

Mockito.when(mockSimple.someMethod()).thenReturn("mocked result");

assertEquals("mocked result", mockSimple.someMethod());

}

```

3. 注解方式mock一个对象:

```java

@Mock

SimpleClass simple;

@Before

public void setUp() {

MockitoAnnotations.initMocks(this);

}

@Test

public void testMock() {

assertNotNull(simple);

}

```

在Java中,我们可以使用Mockito框架来模拟对象并验证其行为。以下是使用Mockito框架进行单元测试的两种方法:

1. 使用`@RunWith(MockitoJUnitRunner.class)`和`@Mock`注解:

```java

import org.junit.Test;

import org.junit.runner.RunWith;

import org.mockito.Mock;

import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)

public class ExampleUnitTest {

@Mock

SimpleClass simple;

@Test

public void testMock() {

assertNotNull(simple);

}

}

```

2. 使用`@Rule`注解和`MockitoRule`类:

```java

import org.junit.Rule;

import org.junit.Test;

import org.mockito.Mock;

import org.mockito.junit.MockitoJUnit;

import org.mockito.junit.MockitoRule;

@Rule //<–使用@Rule

public MockitoRule mockitoRule = MockitoJUnit.rule();

public class ExampleUnitTest {

@Mock

SimpleClass simple;

@Test

public void testMock() {

assertNotNull(simple);

}

}

```

3. 验证对象的行为:

- `verify()`函数的使用:可以对任意一个已mock的对象进行验证,如果该对象的方法被调用了指定次数,则测试通过,否则失败。例如:

```java

import org.junit.Test;

import org.mockito.ArgumentCaptor;

import org.mockito.Mock;

import org.mockito.Verify;

import static org.mockito.Mockito.*;

public class ExampleUnitTest {

@Mock

SimpleClass simple;

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

@Test

public void testMock() throws Exception {

// 当simple的someMethod方法被调用时,捕获参数"some arg"并验证它是否符合预期值"expectedArg"。如果不符合,则测试失败。如果符合,则测试通过。如果simple的someMethod方法没有被调用,则测试失败。这个例子假设SimpleClass有一个名为someMethod的方法,它接受一个String类型的参数。这个方法应该返回一个String类型的值。在这个例子中,我们期望它的返回值是"expectedArg"。如果不是这样,那么测试就会失败。如果是这样,那么测试就会通过。注意,这个例子假设你已经知道你想要验证什么。如果你不知道你想要验证什么,那么你可能需要重新考虑你的设计。例如,你可能需要创建一个接口或者抽象类,然后让SimpleClass实现这个接口或者继承这个抽象类。这样,你就可以在测试中明确地知道你想要验证什么。

通常,为了验证某些行为,我们需要配合一些测试方法。这些方法被称为“打桩方法”(Stub)。打桩的意思是针对模拟出来的对象进行一些模拟操作,例如设置模拟的返回值或抛出异常等。

常见的打桩方法包括:

- `doReturn(Object toBeReturned)`:提前设置要返回的值。

- `doThrow(Throwable... toBeThrown)`:提前设置要抛出的异常。

- `doAnswer(Answer answer)`:提前对结果进行拦截。

- `doCallRealMethod()`:调用某一个方法的真实实现。

- `doNothing()`:设置void函数什么也不做。

- `thenReturn(T value)`:设置要返回的值。

- `thenThrow(Throwable... throwables)`:设置要抛出的异常。

- `thenAnswer(Answer answer)`:对结果进行拦截。

下面是一个使用打桩方法的例子:

```java

@Test

public void testMock() {

// 你可以mock具体的类型,不仅只是接口

List mockedList = mock(List.class);

// 打测试桩

when(mockedList.get(0)).thenReturn("first");

doReturn("aaaa").when(mockedList).get(1);

when(mockedList.get(1)).thenThrow(new RuntimeException());

doThrow(new RuntimeException()).when(mockedList).clear();

// 输出“first”

System.out.println(mockedList.get(0));

// 因为get(999) 没有打桩,因此输出null, 注意模拟环境下这个地方是不会报IndexOutOfBoundsException异常的

System.out.println(mockedList.get(999));

// get(1)时会抛出异常

System.out.println(mockedList.get(1));

}

```

以下是重构后的代码:

```java

// 清除列表

mockedList.clear();

}

doXXXthenXXXint、boolean、String0、falsenull

when(T methodCall)

when(T methodCall)

例如:

when(mock.someMethod()).thenReturn(10);

//你可以使用灵活的参数匹配,例如

when(mock.someMethod(anyString())).thenReturn(10);

//设置抛出的异常

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

//你可以对不同作用的连续回调的方法打测试桩:

//最后面的测试桩(例如:返回一个对象:“foo”)决定了接下来的回调方法以及它的行为。

when(mock.someMethod("some arg"))

.thenReturn("foo") //第一次调用someMethod("some arg")会返回"foo"

.thenThrow(new RuntimeException()); //第二次调用someMethod("some arg")会抛异常

//可以用以下方式替代比较小版本的连贯测试桩:

when(mock.someMethod("some arg"))

.thenReturn("one", "two");

//和下面的方式效果是一样的

when(mock.someMethod("some arg"))

.thenReturn("one")

.thenReturn("two");

//比较小版本的连贯测试桩并且抛出异常:

when(mock.someMethod("some arg"))

.thenThrow(new RuntimeException(), new NullPointerException());

thenAnswer

when(mock.someMethod(anyString())).thenAnswer(new Answer() {

Object answer(InvocationOnMock invocation) {

Object[] args = invocation.getArguments();

```

在Mockito框架中,我们可以使用`doCallRealMethod()`和`doNothing()`方法来控制模拟对象的行为。以下是一个示例:

```java

import org.junit.Test;

import org.mockito.Mockito;

import static org.mockito.Mockito.*;

public class MockitoDemo {

@Test

public void testPerson() {

Person mock = mock(Person.class);

// 当调用getName()方法时,返回“小明”

when(mock.getName()).thenReturn("小明");

System.out.println(mock.getName()); // 输出:小明

// 当调用getName()方法时,返回null

doCallRealMethod().when(mock).getName();

System.out.println(mock.getName()); // 输出:null

}

@Test

public void testVoidMethod() {

Person mock = mock(Person.class);

// 当调用someVoidMethod()方法时,抛出异常

doThrow(new RuntimeException()).when(mock).someVoidMethod();

try {

mock.someVoidMethod(); // 抛出RuntimeException异常

} catch (RuntimeException e) {

System.out.println("捕获到异常:" + e.getMessage()); // 输出:捕获到异常:未知方法调用异常

}

}

}

```

在这个示例中,我们使用了`doCallRealMethod()`和`doNothing()`方法来控制模拟对象的行为。当调用`getName()`方法时,我们设置了模拟返回值为“小明”,所以第一次调用`getName()`方法时会输出“小明”。当我们使用`doCallRealMethod().when(mock).getName()`时,我们告诉Mockito在后续的调用中,应该调用实际的方法而不是模拟的方法。因此,第二次调用`getName()`方法时会输出null。

在Java中,我们可以使用Mockito框架来模拟对象的行为。当我们想要监控一个真实的对象,并且希望这个void方法什么也不做时,可以使用doNothing()函数。以下是一个示例:

```java

import org.mockito.Mockito;

import java.util.LinkedList;

import java.util.List;

public class MockitoExample {

public static void main(String[] args) {

List list = new LinkedList();

List spy = Mockito.spy(list);

// 让clear()什么都不做

Mockito.doNothing().when(spy).clear();

spy.add("one");

// clear()什么都不做,所以列表仍然包含“one”

spy.clear();

}

}

```

当我们想要测试一个无返回值的函数时,可以使用一个含有泛型类Answer参数的doAnswer()函数做回调测试。例如,假设我们有一个void方法有多个回调参数,当我们想指定执行某个回调时,使用thenAnswer很难实现,如果使用doAnswer()将非常简单。以下是一个示例:

```java

import org.mockito.Mockito;

import org.mockito.invocation.InvocationOnMock;

import org.mockito.stubbing.Answer;

public class MockitoExample {

public static void main(String[] args) {

MyCallback callback = Mockito.mock(MyCallback.class);

// 当调用mockedObject.requset(callback)时,执行以下操作

Mockito.doAnswer(new Answer() {

@Override

public Object answer(InvocationOnMock invocation) throws Throwable {

MyCallback call = invocation.getArgument(0);

call.onSuccess();

return null;

}

}).when(mockedObject).requset(callback);

// 当调用callback.onSuccess()时,执行以下操作

Mockito.doAnswer(new Answer() {

@Override

public Object answer(InvocationOnMock invocation) throws Throwable {

System.out.println("onSuccess answer");

return null;

}

}).when(callback).onSuccess();

mockedObject.requset(callback);

}

}

```

当监控真实的对象并且调用真实的函数带来的影响时,可以使用Mockito框架来进行测试。以下是一些常用的方法:

1. 创建一个List对象,并使用spy()方法对其进行监控。然后使用when()和doReturn()方法来模拟真实方法的返回值。例如:

```java

List list = new LinkedList();

List spy = spy(list);

// 不可能完成的:真实方法被调用的时候list仍是空的,所以spy.get(0)会抛出IndexOutOfBoundsException()异常

when(spy.get(0)).thenReturn("foo");

// 这时你应该使用doReturn()函数

doReturn("foo").when(spy).get(0);

```

2. 创建一个SimpleClass对象,并使用mock()方法对其进行监控。然后使用doThrow()方法来模拟真实方法抛出的异常。例如:

```java

SimpleClass mock = mock(SimpleClass.class);

doThrow(new RuntimeException()).when(mock).someVoidMethod();

mock.someVoidMethod();

```

3. 在受监控的对象上测试函数。可以多次测试同一个函数,在测试过程中改变mock对象的行为。例如:

```java

mock.someMethod("some arg");

mock.someMethod("another arg");

```

4. 验证方法的调用次数。可以使用times()、never()、only()、atLeast()、atMost()、after()、timeout()、description()、verifyZeroInteractions等方法进行验证。例如:

```java

mock.someMethod("some arg");

mock.someMethod("another arg");

```

```java

// 创建一个Person对象的mock

Person person = mock(Person.class);

// 调用mPerson.getAge()方法两次

person.getAge();

person.getAge();

// 验证至少调用2次

verify(person, atLeast(2)).getAge();

// 验证至多调用2次

verify(person, atMost(2)).getAge();

// 验证调用次数为0

verify(person, never()).getAge();

// 验证方法在100ms超时前被调用2次

verify(person, timeout(100).times(2));

// 使用Mockito框架进行验证

verify(mock, times(2)).someMethod("some arg");

// 如果mock.someMethod("some arg")没有被连续调用两次,则验证失败

verify(mock, times(2)).someMethod("some arg");

```

```java

//由于person对象发生了交互,所以这里验证失败,把上面的调用注释掉这里就会验证成功

verifyZeroInteractions(person);

//可以验证多个对象没有交互

//verifyZeroInteractions(person,person2 );

}

@Test

public void testVerifyZeroInteractions() {

Person person = mock(Person.class);

person.eat("a");

verify(person).eat("a");

// 注意,这将会无法到达验证目的,不能跟verify()混用

// verifyZeroInteractions(person,person2 );

}

5. 参数匹配器 (matchers)

Mockito以自然的java风格来验证参数值: 使用equals()函数。有时,当需要额外的灵活性时你可能需要使用参数匹配器,也就是argument matchers :

// 使用内置的anyInt()参数匹配器

when(mockedList.get(anyInt())).thenReturn("element");

// 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 )

when(mockedList.contains(argThat(isValid()))).thenReturn("element");

// 输出element

System.out.println(mockedList.get(999));

// 你也可以验证参数匹配器

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

常用的参数匹配器:

| 方法名 | 含义 |

| :-- | :-- |

| anyObject() | 匹配任何对象 |

| any(Class type) | 与anyObject()一样 |

| any() | 与anyObject()一样 |

| anyBoolean() | 匹配任何boolean和非空Boolean |

| anyByte() | 匹配任何byte和非空Byte |

| anyCollection() | 匹配任何非空Collection |

```

以下是重构后的代码:

```java

import org.hamcrest.core.Is;

import org.junit.Test;

import static org.junit.Assert.*;

public class TestMatcher {

@Test

public void testPersonAny() {

// 匹配任何double和非空Double

when(mPerson.eat(anyDouble())).thenReturn("米饭");

// 或:

// when(mPerson.eat(anyFloat())).thenReturn("米饭");

// 输出米饭

System.out.println(mPerson.eat(3.14));

}

@Test

public void testPersonContains() {

// 参数包含给定的substring字符串

when(mPerson.eat(contains("面"))).thenReturn("面条");

// 输出面条

System.out.println(mPerson.eat("面"));

}

@Test

public void testPersonArgThat() {

// 自定义输入字符长度为偶数时,输出面条。

when(mPerson.eat(argThat(new Is("输入字符长度必须为偶数")))).thenReturn("面条");

// 输出面条

System.out.println(mPerson.eat("1234"));

}

}

```

需要注意的是,如果你打算使用参数匹配器,那么所有参数都必须由匹配器提供。例如:

```java

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));

// 上述代码是正确的,因为eq()也是一个参数匹配器

verify(mock).someMethod(anyInt(), anyString(), "third argument");

// 上述代码是错误的, 因为所有参数必须由匹配器提供,而参数"third argument"并非由参数匹配器提供,因此会抛出异常

```

像anyObject(), eq()这样的匹配器函数不会返回匹配器。它们会在内部将匹配器记录到一个栈当中,并且返回一个假的值,通常为null。

6. 使用InOrder验证执行顺序

InOrder用于验证mock对象的函数执行顺序。例如,验证mock一个对象的函数执行顺序:

```java

@Test

public void testInorder() {

List singleMock = mock(List.class);

singleMock.add("小明");

singleMock.add("小红");

// 为该mock对象创建一个inOrder对象

InOrder inOrder = inOrder(singleMock);

// 验证add函数首先执行的是add("小明"),然后才是add("小红"),否则测试失败

inOrder.verify(singleMock).add("小明");

inOrder.verify(singleMock).add("小红");

}

```

验证多个mock对象的函数执行顺序:

```java

@Test

public void testInorderMulti() {

List firstMock = mock(List.class);

List secondMock = mock(List.class);

firstMock.add("小明");

secondMock.add("小红");

// 为这两个Mock对象创建inOrder对象

InOrder inOrder = inOrder(firstMock, secondMock);

// 验证它们的执行顺序

inOrder.verify(firstMock).add("小明");

}

```

在单元测试中,我们经常需要验证方法的参数是否符合预期。这时,可以使用Mockito框架中的`ArgumentCaptor`类来捕获方法的参数。以下是一个使用`ArgumentCaptor`进行参数捕获的示例:

```java

import static org.mockito.Mockito.*;

import java.util.List;

import org.junit.Test;

import org.mockito.ArgumentCaptor;

import org.mockito.Captor;

public class ArgumentCaptorTest {

@Captor

ArgumentCaptor captor;

@Test

public void testArgumentCaptor() {

List list = new ArrayList<>();

String value = "test";

when(list.add(captor.capture())).thenReturn(true);

boolean result = list.add(value);

assertEquals(true, result);

assertEquals(value, captor.getValue());

}

}

```

在这个示例中,我们使用`@Captor`注解创建了一个`ArgumentCaptor`对象,用于捕获`list.add()`方法的参数。然后,在测试方法中,我们调用`list.add()`方法,并将捕获到的参数值与预期值进行比较。这样,我们就可以验证方法的参数是否符合预期。

参数捕获主要是为了方便下一步的断言操作,示例代码如下:

```java

@Test

public void argumentCaptorTest() {

List mock = mock(List.class);

mock.add("John");

// 构建要捕获的参数类型,这里是String

ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);

// 在verify方法的参数中调用argument.capture()方法来捕获输入的参数

verify(mock).add(argument.capture());

// 验证“John”参数捕获

assertEquals("John", argument.getValue());

}

@Test

public void argumentCaptorTest2() {

List mock = mock(List.class);

mock.add("Brian");

mock.add("Jim");

ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);

verify(mock, times(2)).add(argument.capture());

// 如果有多次参数调用,argument.getValue()捕获到的是最后一次调用的参数

assertEquals("Jim", argument.getValue());

// 如果需要获取所有参数值,可以调用argument.getAllValues().toArray()方法

Object[] values = argument.getAllValues().toArray();

assertEquals("Brian", values[0]);

assertEquals("Jim", values[1]);

}

```

Mock

Address mAddress;

@Test

public void argumentInjectMock() {

when(mAddress.getDetail()).thenReturn("浙江杭州");

System.out.println(mTestUser.getAddress());

}