一、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
```
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
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
@Test
// 当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
@Test
public void testArgumentCaptor() {
List
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
// 在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
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());
}