Mockito 教程:从入门到精通,掌握Java单元测试利器
这是文章《Mockito 教程》的第1部分(共1部分)。
Mockito 是一个基于 Java 的模拟框架,常与 JUnit 和 TestNG 等测试框架配合使用。它内部利用 Java 反射 API,能够创建服务对象。模拟对象返回虚拟数据,从而避免了对外部依赖的真实调用。通过模拟外部依赖并将其应用于待测试的代码,Mockito 极大地简化了测试的开发过程。
Mockito 教程
在本 Mockito 教程中,我们将使用 JUnit 5 来创建一些需要模拟的服务。
Mockito Maven 依赖
要在项目中实现基于 Mockito 的测试用例,您需要将以下依赖项添加到项目的 pom.xml
文件中:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.19.0</version>
<scope>test</scope>
</dependency>
请注意,对于 JUnit 5,需要使用 mockito-junit-jupiter
库。如果您使用的是其他测试框架,如 JUnit 4 或 TestNG,则应移除此依赖项,并仅包含 mockito-core
库。
Mockito 模拟对象的创建
Mockito 框架允许我们使用 @Mock
注解或 mock()
静态方法创建模拟对象。
Mockito 的 mock()
方法
下面的例子展示了 mock()
方法的使用。
package com.Olivia.mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import com.Olivia.AddService;
import com.Olivia.CalcService;
public class CalcService1Test {
@Test
void testCalc() {
System.out.println("**--- Test testCalc executed ---**");
AddService addService;
CalcService calcService;
addService = Mockito.mock(AddService.class);
calcService = new CalcService(addService);
int num1 = 11;
int num2 = 12;
int expected = 23;
when(addService.add(num1, num2)).thenReturn(expected);
int actual = calcService.calc(num1, num2);
assertEquals(expected, actual);
}
}
在上述示例中,我们正在测试 CalcService
。使用 Mockito.mock()
方法创建了一个 AddService
类的模拟对象。
Mockito 框架的 @Mock
注解
以下示例展示了 @Mock
注解的用法。
package com.Olivia.mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.Olivia.AddService;
import com.Olivia.CalcService;
public class CalcService2Test {
CalcService calcService;
@Mock
AddService addService;
@BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testCalc() {
System.out.println("**--- Test testCalc executed ---**");
calcService = new CalcService(addService);
int num1 = 11;
int num2 = 12;
int expected = 23;
when(addService.add(num1, num2)).thenReturn(expected);
int actual = calcService.calc(num1, num2);
assertEquals(expected, actual);
}
}
请注意,我们需要调用 MockitoAnnotations.initMocks(this)
来初始化使用 @Mock
、@Spy
、@Captor
或 @InjectMocks
注解的对象。
模拟行为验证 – Mockito
在使用 when()
和 thenReturn()
函数时,可以为模拟类添加行为。这意味着当调用模拟对象 (addService
) 的 add
方法并传入 (num1, num2)
参数时,它将返回存储在 expected
变量中的值。我们的 CalcService
类如下所示:
public class CalcService {
private AddService addService;
public CalcService(AddService addService) {
this.addService = addService;
}
public int calc(int num1, int num2) {
System.out.println("**--- CalcService calc executed ---**");
return addService.add(num1, num2);
}
}
CalcService
对 AddService
类有一个依赖。它使用 AddService
类的 add
方法来执行操作。由于我们只想对 CalcService
类进行单元测试,所以我们必须模拟 AddService
实例。AddService
的接口如下:
public interface AddService {
public int add(int num1, int num2);
}
public class AddServiceImpl implements AddService {
@Override
public int add(int num1, int num2) {
System.out.println("**--- AddServiceImpl add executed ---**");
return num1 + num2;
}
}
Mockito 验证交互
Mockito 框架会跟踪所有方法调用及其传递给模拟对象的参数。Mockito 的 verify()
方法用于验证某个方法是否以指定的参数进行调用。我们还可以指定调用次数的逻辑,例如确切的调用次数、至少指定的调用次数、少于指定的调用次数等。我们可以使用 VerificationModeFactory
来处理调用次数的逻辑。Mockito 的 verify()
方法用于验证某个方法是否以正确的参数进行调用。它不像 assert
方法那样检查方法调用的结果。下面的示例展示了 verify()
方法的使用方式:
package com.Olivia.mockito;
import static org.mockito.Mockito.verify;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.internal.verification.VerificationModeFactory;
public class VerifyInteractionTest {
@Test
public void testMethod() {
@SuppressWarnings("unchecked")
List<String> mockedList = Mockito.mock(List.class);
mockedList.add("first-element");
mockedList.add("second-element");
mockedList.add("third-element");
mockedList.add("third-element");
mockedList.clear();
verify(mockedList).add("first-element");
verify(mockedList).add("second-element");
verify(mockedList, VerificationModeFactory.times(2)).add("third-element");
verify(mockedList).clear();
}
}
使用 Mockito 对具体类进行存根化
使用 when() - thenReturn()
函数,我们可以对具体/实现类和集合的单个元素进行存根操作。未存根的元素将包含空值。
package com.Olivia.mockito;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MockSingleElementTest {
@SuppressWarnings("unchecked")
@Test
public void testMethod() {
ArrayList mockedList = mock(ArrayList.class);
when(mockedList.get(0)).thenReturn("first-element");
System.out.println(mockedList.get(0));
assertEquals("first-element", mockedList.get(0));
// "null" gets printed as get(1) is not stubbed
System.out.println(mockedList.get(1));
}
}
Mockito Spy(间谍对象)
当您调用一个被监视对象(Spy)的方法时,除非预先定义了行为,否则将调用真实的方法。使用 Spy,我们可以通过 when()-thenReturn()
函数来定义行为,或者直接调用真实的实现。
package com.Olivia.mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
public class MockitoSpyTest {
@Test
public void testMethod() {
List<String> list = new ArrayList<>();
List<String> listSpy = spy(list);
listSpy.add("first-element");
System.out.println(listSpy.get(0));
assertEquals("first-element", listSpy.get(0));
when(listSpy.get(0)).thenReturn("second-element");
System.out.println(listSpy.get(0));
assertEquals("second-element", listSpy.get(0));
}
}
结论
Mockito 是用于 Java 单元测试的流行模拟框架。我们可以很容易地使用 Mockito 对依赖进行模拟。Mockito 的编码风格流畅且类似于 JUnit 和 TestNG 框架,因此学习曲线很小。
您可以从我们的 GitHub 仓库下载完整的项目代码。