Mockito模拟示例

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

Mockito模拟框架提供了模拟类的不同方法。
让我们看一下可以模拟类并显示其行为的不同方法。

Mockito模拟方法

我们可以使用Mockito类的mock()方法创建给定类或者接口的模拟对象。
这是模拟对象的最简单方法。

package com.theitroad.mockito.mock;

import java.util.List;

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

public class MockitoMockMethodExample {

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//using Mockito.mock() method
		List<String> mockList = mock(List.class);
		when(mockList.size()).thenReturn(5);
		assertTrue(mockList.size()==5);
	}
	
}

我们使用JUnit 5与Mockito一起编写测试用例来模拟对象。

Mockito @模拟注解

我们也可以使用@Mock注释来模拟对象。
当我们想在多个地方使用模拟对象时,这很有用,因为我们避免了多次调用mock()方法。
代码变得更具可读性,我们可以指定模拟对象名称,以防出现错误。

使用@Mock注释时,必须确保调用MockitoAnnotations.initMocks(this)来初始化模拟对象。
我们可以在测试之前执行的测试框架设置方法中执行此操作。

package com.theitroad.mockito.mock;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class MockitoMockAnnotationExample {

	@Mock
	List<String> mockList;
	
	@BeforeEach
	public void setup() {
		//if we don't call below, we will get NullPointerException
		MockitoAnnotations.initMocks(this);
	}
	
	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		when(mockList.get(0)).thenReturn("theitroad");
		assertEquals("theitroad", mockList.get(0));
	}
	
}

Mockito @InjectMocks批注

当我们要将一个模拟对象注入另一个模拟对象时,可以使用@InjectMocks批注。
@InjectMock创建类的模拟对象,并将带有注释@Mock的模拟注入到该类中。

package com.theitroad.mockito.mock;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class MockitoInjectMockAnnotationExample {

	@Mock
	List<String> mockList;
	
	//@InjectMock creates an instance of the class and 
	//injects the mocks that are marked with the annotations @Mock into it.
	@InjectMocks
	Fruits mockFruits;
	
	@BeforeEach
	public void setup() {
		//if we don't call below, we will get NullPointerException
		MockitoAnnotations.initMocks(this);
	}
	
	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		when(mockList.get(0)).thenReturn("Apple");
		when(mockList.size()).thenReturn(1);
		assertEquals("Apple", mockList.get(0));
		assertEquals(1, mockList.size());
		
		//mockFruits names is using mockList, below asserts confirm it
		assertEquals("Apple", mockFruits.getNames().get(0));
		assertEquals(1, mockFruits.getNames().size());	
		
		mockList.add(1, "Mango");
		//below will print null because mockList.get(1) is not stubbed
		System.out.println(mockList.get(1));
	}
	
}

class Fruits{
	private List<String> names;

	public List<String> getNames() {
		return names;
	}

	public void setNames(List<String> names) {
		this.names = names;
	}
	
}

Mockito spy()进行部分模拟

如果我们只想模拟特定的行为,并调用未受干扰行为的真实方法,则可以使用Mockito spy()方法创建一个间谍对象。

package com.theitroad.mockito.mock;

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 MockitoSpyMethodExample {

	@Test
	public void test() {
		List<String> list = new ArrayList<>();
		List<String> spyOnList = spy(list);
		
		when(spyOnList.size()).thenReturn(10);
		assertEquals(10, spyOnList.size());
		
		//calling real methods since below methods are not stubbed
		spyOnList.add("hyman");
		spyOnList.add("Meghna");
		assertEquals("hyman", spyOnList.get(0));
		assertEquals("Meghna", spyOnList.get(1));
	}
	
}

Mockito @Spy注释

我们可以使用@Spy注释监视对象。
让我们看一个简单的例子。

package com.theitroad.mockito.mock;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

public class MockitoSpyAnnotationExample {

	@Spy
	Utils mockUtils;
	
	@BeforeEach
	public void setup() {
		MockitoAnnotations.initMocks(this);
	}
	
	@Test
	public void test() {
		when(mockUtils.process(1, 1)).thenReturn(5);
		//mocked method
		assertEquals(5, mockUtils.process(1, 1));
		//real method called since it's not stubbed
		assertEquals(20, mockUtils.process(19, 1));
		
	}
	
}

class Utils{
	public int process(int x, int y) {
		System.out.println("Input Params = "+x+","+y);
		return x+y;
	}
}

请注意,@ Spy批注会尝试调用no-args构造函数以初始化模拟对象。
如果您的程序没有该程序,则会收到以下错误。

org.mockito.exceptions.base.MockitoException: Unable to initialize @Spy annotated field 'mockUtils'.
Please ensure that the type 'Utils' has a no-arg constructor.
	at com.theitroad.mockito.mock.MockitoSpyAnnotationExample.setup(MockitoSpyAnnotationExample.java:18)

另外,请注意,Mockito无法实例化内部类,局部类,抽象类和接口。
因此,最好提供一个实例进行监视。
否则,实际的方法可能不会被调用并被静默忽略。

例如,如果您指定一个间谍对象,如下所示:

@Spy
List<String> spyList;

您会注意到,当您调用add()或者get()方法时,不会调用实际方法。
如果您指定如下所示的间谍对象,那么一切都会正常进行。

@Spy
List<String> spyList = new ArrayList<>();