如何模拟被测试方法使用的 Bean
How to Mock a Bean Used by the Method being tested
这是一个配置 class,我在其中创建了一个自定义时钟 Bean
...
import java.time.Clock;
import org.springframework.context.annotation.Bean;
public class MyConfig {
@Bean("customClock")
public Clock clock() {
if (hasCustomClockProperty()) {
// here is where I build my custom clock if configured
} else {
return Clock.systemDefaultZone();
}
}
}
... 下面是我在实际代码中使用它的方式:
import java.time.Clock;
import org.springframework.beans.factory.annotation.Qualifier;
public class MyClass {
@Qualifier("customClock")
private final Clock clock;
public LocalDateTime getCustomLocalDateTime() {
return LocalDateTime.now(clock);
}
}
当我使用 Mockito 对 getCustomLocalDateTime
进行单元测试时,它失败了,因为 clock
为空...那么我该如何模拟呢?下面是我的尝试......但当然它不起作用:
import java.time.Clock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Qualifier;
@RunWith(MockitoJUnitRunner.class)
public class MyTestClass {
@Qualifier("customClock")
private Clock clock;
@InjectMocks private MyClass myClass;
@Test
public void getCustomLocalDateTime_ok() {
...
LocalDateTime now = myClass.getCustomLocalDateTime();
// this fails with NullPointerException because the clock bean is null
...
}
}
任何帮助将不胜感激。
你应该使用构造函数参数注入。这样,您只需手动创建一个 customClock class 并将其传递给构造函数。 Spring 默认情况下将自动装配 bean 构造函数的任何缺失参数。
public class MyClass {
private final Clock clock;
public MyClass(Clock customClock)
{
this.clock = customClock;
}
public LocalDateTime getCustomLocalDateTime() {
return LocalDateTime.now(clock);
}
}
测试看起来像这样:
public class MyTestClass {
@Test
public void getCustomLocalDateTime_ok() {
MyConfig config = new MyConfig();
MyClass myClass = new MyClass(config.clock());
LocalDateTime result = myClass.getCustomLocalDateTime();
// validate the result
}
}
一般来说,您应该始终避免模拟 bean。在大多数情况下,它是可以避免的,并且通常会产生更好、更易读且更少的 error-prone 代码。
正如 M. Deinum 所说,您需要 @Mock
注释 Mockito 注入的内容。
示例:
class MyTestClass {
@Mock
private Clock clock;
@InjectMocks
private MyClass myClass;
@BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
但是,您的模拟有一个警告。 Clock 有 static
方法,我不确定 mockito 是否可以在没有模拟静态方法的情况下模拟它正常工作(示例:Mocking static methods with Mockito)。这取决于您的测试使用什么。
这是一个配置 class,我在其中创建了一个自定义时钟 Bean
...
import java.time.Clock;
import org.springframework.context.annotation.Bean;
public class MyConfig {
@Bean("customClock")
public Clock clock() {
if (hasCustomClockProperty()) {
// here is where I build my custom clock if configured
} else {
return Clock.systemDefaultZone();
}
}
}
... 下面是我在实际代码中使用它的方式:
import java.time.Clock;
import org.springframework.beans.factory.annotation.Qualifier;
public class MyClass {
@Qualifier("customClock")
private final Clock clock;
public LocalDateTime getCustomLocalDateTime() {
return LocalDateTime.now(clock);
}
}
当我使用 Mockito 对 getCustomLocalDateTime
进行单元测试时,它失败了,因为 clock
为空...那么我该如何模拟呢?下面是我的尝试......但当然它不起作用:
import java.time.Clock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Qualifier;
@RunWith(MockitoJUnitRunner.class)
public class MyTestClass {
@Qualifier("customClock")
private Clock clock;
@InjectMocks private MyClass myClass;
@Test
public void getCustomLocalDateTime_ok() {
...
LocalDateTime now = myClass.getCustomLocalDateTime();
// this fails with NullPointerException because the clock bean is null
...
}
}
任何帮助将不胜感激。
你应该使用构造函数参数注入。这样,您只需手动创建一个 customClock class 并将其传递给构造函数。 Spring 默认情况下将自动装配 bean 构造函数的任何缺失参数。
public class MyClass {
private final Clock clock;
public MyClass(Clock customClock)
{
this.clock = customClock;
}
public LocalDateTime getCustomLocalDateTime() {
return LocalDateTime.now(clock);
}
}
测试看起来像这样:
public class MyTestClass {
@Test
public void getCustomLocalDateTime_ok() {
MyConfig config = new MyConfig();
MyClass myClass = new MyClass(config.clock());
LocalDateTime result = myClass.getCustomLocalDateTime();
// validate the result
}
}
一般来说,您应该始终避免模拟 bean。在大多数情况下,它是可以避免的,并且通常会产生更好、更易读且更少的 error-prone 代码。
正如 M. Deinum 所说,您需要 @Mock
注释 Mockito 注入的内容。
示例:
class MyTestClass {
@Mock
private Clock clock;
@InjectMocks
private MyClass myClass;
@BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
但是,您的模拟有一个警告。 Clock 有 static
方法,我不确定 mockito 是否可以在没有模拟静态方法的情况下模拟它正常工作(示例:Mocking static methods with Mockito)。这取决于您的测试使用什么。