如何模拟被测试方法使用的 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)。这取决于您的测试使用什么。