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.