Mockito 教程

Mockito是一个基于Java的模拟框架,与其他测试框架如JUnit和TestNG一起使用。它内部使用Java反射API,并允许创建服务对象。模拟对象返回虚拟数据并避免外部依赖。通过模拟外部依赖并将模拟应用于待测试的代码,它简化了测试的开发过程。

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的Mock创建

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,我们可以通过使用when()-theReturn()函数来定义行为,或者可以调用真实的实现。

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仓库下载完整的项目代码。

bannerAds