Mockito @InjectMocks注解完全指南:Java单元测试中的依赖注入模拟

使用Mockito的@InjectMocks注解可以在被注解的类中注入模拟依赖的对象。当我们需要对一个类进行模拟时,如果该类有外部依赖,我们可以使用@Mock或@Spy注解来指定要注入的模拟对象。

Mockito @InjectMocks 注解详解

Mockito尝试使用三种不同的方法注入模拟的依赖项,并按照指定的顺序进行:

  1. 构造器注入 – 当类定义有构造器时,Mockito会尝试使用最大的构造器来注入依赖项。
  2. 基于Setter方法 – 当没有定义构造器时,Mockito会尝试使用Setter方法来注入依赖项。
  3. 基于字段 – 如果无法进行构造器或基于Setter的注入,那么Mockito将尝试直接注入依赖项到字段本身。

如果只有一个匹配的模拟对象,那么Mockito将注入该对象。如果有多个相同类的模拟对象,则使用模拟对象名称来注入依赖项。

@InjectMocks 使用示例

让我们创建一些具有依赖关系的服务和类,以便我们可以看到Mockito依赖注入模拟的实际效果。

服务类

package com.Olivia.injectmocksservices;

// 服务接口
public interface Service {

	public boolean send(String msg);
}
package com.Olivia.injectmocksservices;

// 邮件服务实现
public class EmailService implements Service {

	@Override
	public boolean send(String msg) {
		System.out.println("发送邮件");
		return true;
	}

}
package com.Olivia.injectmocksservices;

// 短信服务实现
public class SMSService implements Service {

	@Override
	public boolean send(String msg) {
		System.out.println("发送短信");
		return true;
	}
}

具有依赖项的应用程序服务类

package com.Olivia.injectmocksservices;

// 基于构造函数的@InjectMocks注入
public class AppServices {

	private EmailService emailService;
	private SMSService smsService;

	public AppServices(EmailService emailService, SMSService smsService) {
		this.emailService = emailService;
		this.smsService = smsService;
	}

	public boolean sendSMS(String msg) {
		return smsService.send(msg);
	}

	public boolean sendEmail(String msg) {
		return emailService.send(msg);
	}
}
package com.Olivia.injectmocksservices;

// 基于属性Setter方法的@InjectMocks注入
public class AppServices1 {

	private EmailService emailService;
	private SMSService smsService;

	public void setEmailService(EmailService emailService) {
		this.emailService = emailService;
	}

	public void setSmsService(SMSService smsService) {
		this.smsService = smsService;
	}

	public boolean sendSMS(String msg) {
		return smsService.send(msg);
	}

	public boolean sendEmail(String msg) {
		return emailService.send(msg);
	}

}
package com.Olivia.injectmocksservices;

// 基于字段的@InjectMocks注入
public class AppServices2 {

	private EmailService emailService;
	private SMSService smsService;

	public boolean sendSMS(String msg) {
		return smsService.send(msg);
	}

	public boolean sendEmail(String msg) {
		return emailService.send(msg);
	}

}

@InjectMocks 构造函数注入示例

package com.Olivia.mockito.injectmocks;

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

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

import com.Olivia.injectmocksservices.AppServices;
import com.Olivia.injectmocksservices.AppServices1;
import com.Olivia.injectmocksservices.AppServices2;
import com.Olivia.injectmocksservices.EmailService;
import com.Olivia.injectmocksservices.SMSService;

class MockitoInjectMocksExamples extends BaseTestCase {

	@Mock EmailService emailService;
	
	@Mock SMSService smsService;
	
	@InjectMocks AppServices appServicesConstructorInjectionMock;
	
	@InjectMocks AppServices1 appServicesSetterInjectionMock;
	
	@InjectMocks AppServices2 appServicesFieldInjectionMock;
	
	@Test
	void test_constructor_injection_mock() {
		when(appServicesConstructorInjectionMock.sendEmail("邮件")).thenReturn(true);
		when(appServicesConstructorInjectionMock.sendSMS(anyString())).thenReturn(true);
		
		assertTrue(appServicesConstructorInjectionMock.sendEmail("邮件"));
		assertFalse(appServicesConstructorInjectionMock.sendEmail("未模拟的邮件"));
		
		assertTrue(appServicesConstructorInjectionMock.sendSMS("短信"));
		
	}
}

你有没有注意到我的测试类继承了BaseTestCase。这是为了在测试之前初始化Mockito的模拟对象,以下是该类的代码。

package com.Olivia.mockito.injectmocks;

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

class BaseTestCase {

	@BeforeEach
	void init_mocks() {
		MockitoAnnotations.initMocks(this);
	}

}

如果您不调用MockitoAnnotations.initMocks(this);,那么您将会得到NullPointerException。另外,我正在使用JUnit 5来运行测试用例。如果您对此不熟悉,请查看JUnit 5教程。

@InjectMocks Setter方法注入示例

@Test
void test_setter_injection_mock() {
	when(appServicesSetterInjectionMock.sendEmail("新邮件")).thenReturn(true);
	when(appServicesSetterInjectionMock.sendSMS(anyString())).thenReturn(true);
	
	assertTrue(appServicesSetterInjectionMock.sendEmail("新邮件"));
	assertFalse(appServicesSetterInjectionMock.sendEmail("未模拟的邮件"));
	
	assertTrue(appServicesSetterInjectionMock.sendSMS("短信"));
	
}

@InjectMocks 基于字段的注入示例

@Test
void test_field_injection_mock() {
	when(appServicesFieldInjectionMock.sendEmail(anyString())).thenReturn(true);
	when(appServicesFieldInjectionMock.sendSMS(anyString())).thenReturn(true);
	
	assertTrue(appServicesFieldInjectionMock.sendEmail("邮件"));
	assertTrue(appServicesFieldInjectionMock.sendEmail("新邮件"));
	
	assertTrue(appServicesFieldInjectionMock.sendSMS("短信"));
	
}

您可以从我们的GitHub代码库中查看完整的代码和更多的Mockito示例。

参考:API文档

bannerAds