Home > other >  How to Mock a Bean Used by the Method being tested
How to Mock a Bean Used by the Method being tested

Time:02-15

Here is a configuration class where I create a custom clock 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();
    }
  }
}

... and here is how I use it in my actual code:

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);
  }
}

When I unit test getCustomLocalDateTime with Mockito, it fails because clock is null... so how do I mock this? Here below is my try... but of course it doesn't work:

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
    ...
  }
}

Any help would be really appreciated.

CodePudding user response:

you should use contructor parameter injection. That way, you can just create a customClock class manually and pass it to the constructor. Spring will autowire any missing parameters of constructors of a bean by default.

public class MyClass {
  private final Clock clock;
  public MyClass(Clock customClock)
  {
    this.clock = customClock;
  }

  public LocalDateTime getCustomLocalDateTime() {
    return LocalDateTime.now(clock);
  }
}

And the test would look like this:

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
  }
}

In general, you should always avoid mocking beans. It is in most cases avoidable and usually yields better, more readable and less error-prone code.

CodePudding user response:

As M. Deinum said, you need a @Mock annotation on what is to be injected by Mockito.

Example:

class MyTestClass {

  @Mock
  private Clock clock;
  @InjectMocks
  private MyClass myClass;

  @BeforeEach
  void setUp() {

    MockitoAnnotations.initMocks(this);
  }

But, there is a caveat on your mock. Clock has static methods, and I'm not sure if mockito can mock it to work right without the mock static approach (example: Mocking static methods with Mockito). It depends on what your tests use.

  • Related