Mockito教程

时间:2020-02-23 14:40:45  来源:igfitidea点击:

Mockito是基于Java的模拟框架,可与其他测试框架(例如JUnit和TestNG)结合使用。

它在内部使用Java Reflection 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模拟创作

Mockito框架允许我们使用@Mock批注或者static方法mock()创建模拟对象。

Mockito mock()方法

下面的示例演示了mock()方法的用法:

package com.theitroad.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.theitroad.AddService;
import com.theitroad.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注释的用法。

package com.theitroad.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.theitroad.AddService;
import com.theitroad.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()函数添加行为。
这意味着当使用(num1,num2)参数为添加方法调用模拟对象(addService)时,它将返回存储在预期变量中的值。

我们的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框架跟踪对模拟对象的所有方法调用及其参数。
模拟对象上的Mockitoverify()方法验证使用特定参数调用的方法。
我们还可以指定调用逻辑的数量,例如确切的次数,至少指定的次数,小于指定的次数等。
我们可以将" VerificationModeFactory"用于调用次数逻辑。

Mockito verify()方法检查是否使用正确的参数调用了一个方法。
它不会检查类似assert方法的方法调用的结果。
下面的示例演示了verify()方法的用法:

package com.theitroad.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 Stub具体程序

使用when()– thenReturn()函数,我们可以对具体/实现类以及集合的单个元素进行存根。
非存根元素将其中包含null。

package com.theitroad.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<String> 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对象的方法时,除非定义了预定义的行为,否则将调用real方法。
使用 Spy,我们可以通过when()-theReturn()函数定义行为,或者可以调用实际实现。

package com.theitroad.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));
	}

}