JUnit Java Instant dependent tests - java

I'm using Instant.now() to get the current UTC millis, and then truncate it to the nearest hour. All my JUnit are failing because they are taking the current System time. How can I make Instant.now() return a fixed value which I could supply in the JUnit test.
public static Long getCurrentHour() {
Instant now = Instant.now();
Instant cH = now.truncatedTo(ChronoUnit.HOURS);
return cH.toEpochMilli();
}

You should mock the static method Instant.now() to give you a static instant value.
You can use PowerMockito for that.
#RunWith(PowerMockRunner.class)
#PrepareForTest(Instant.class)
public class TestClass {
#Mock private Instant mockInstant;
#Test
public void getCurrentHour() throws Exception {
PowerMockito.mockStatic(Instant.class);
when(Instant.now()).thenReturn(mockInstant);
when(mockInstant.truncatedTo(ChronoUnit.HOURS)).thenReturn(mockInstant);
long expectedMillis = 999l;
when(mockInstant.toEpochMilli()).thenReturn(expectedMillis);
assertEquals(expectedMillis, YourClass.getCurrentHour());
}
}

Related

Service in Spring not setting values with Junit

Im testing my service with Junit but the result is not the expected.
When i save my entity, the return date is not setted in service.
Test:
#Test
#DisplayName("Should set determined time for return date")
public void shouldSetReturnDate() {
ClientDTORequest dto = createNewDTOClient();
Client client = createNewClient();
Mockito.when(clientRepository.save(Mockito.any())).thenReturn(client);
Client saved = clientService.save(dto);
Assertions.assertEquals(dateTimeNow.plusMinutes(30), saved.getReturnDate());
}
My createNewClient():
private Client createNewClient() {
//the null param is the return date
return new Client(1L, "name", null);
}
My service:
public Client save(ClientDTORequest dto) {
Client client = mapper.map(dto, Client.class);
client.setReturnDate(dateTimeNow.plusMinutes(30));
Client savedClient = clientRepository.save(client);
return savedClient;
}
And when the test result:
org.opentest4j.AssertionFailedError:
Expected :2022-04-04T01:17:25.715895900
Actual :null
The result is not passed by the service to mock, this is my shot, but i dont know why.
Thanks!
The problem is you're coupled to "now", so the service always will have the time at the moment it runs.
One of the best ways of work with time is by modeling the concept Clock or TimeProvider and injecting it to the Service. Then you can mock it to assert the time in the test.
class Clock {
LocalDateTime now() {
return LocalDateTime.now().plusMinutes(30); // <-- as you needs
}
}
class Service {
private Clock clock;
Service(Clock clock) {
this.clock = clock;
}
void save(MyEntity entity) {
entity.setCreatedDateTime(clock.now());
//repositoty.save(entity);
}
}
#Getter
class MyEntity {
private LocalDateTime createdDateTime;
public void setCreatedDateTime(LocalDateTime createdDateTime) {
//assing it to a field
this.createdDateTime = createdDateTime;
}
}
class ServiceTest {
#Mock
private Clock clock;
private Service service;
#Test
void testSave() {
LocalDateTime fixedDateTimeNow = LocalDateTime.of(2022, 4, 3, 18, 0, 0);
Mockito.when(clock.now()).thenReturn(fixedDateTimeNow);
MyEntity entity = new MyEntity();
service.save(entity);
Assertions.assertEquals(fixedDateTimeNow, entity.getCreatedDateTime());
}
}
Note: Be careful about holding state in your service, so it's not thread safe. So, you'll end up with concurrency problems when multiple calls to service occurs "at the same time".
If you injected your clientRepository with #Autowired then it won't mock. Try #SpyBean
(#Autowired ClientRepository clientRepository wouldn't mock; #SpyBean ClientRepository clientRepository should mock)
After some hours of testing i found the problem:
My service was changing data, but was overridden by my mock:
Mockito.when(clientRepository.save(Mockito.any())).thenReturn(client); <-- mock overridden the changed data from service
Client saved = clientService.save(dto);
So i found ArgumentCaptor, where i can get the object from method call:
Declaring the Captor:
#Captor
ArgumentCaptor<Client> clientCaptor;
Using at test method:
Mockito.when(clientRepository.save(clientCaptor.capture())).thenReturn(client); //<-- capturing the result
clientService.save(dto);
Client saved = clientCaptor.getValue() //getting object
Assertions.assertEquals(dto.getReturnDate().plusMinutes(30), saved.getReturnDate()); //assertion

How to Mock a ZonedDateTime with Mockito and Junit

I need to mock a ZonedDateTime.ofInstant() method. I know there are many suggestions in SO but for my specific problem, till now I did not get any simple way out.
Here is my code :
public ZonedDateTime myMethodToTest(){
MyClass myClass;
myClass = fetchSomethingFromDB();
try{
final ZoneId systemDefault = ZoneId.systemDefault();
return ZonedDateTime.ofInstant(myClass.getEndDt().toInstant(), systemDefault);
} catch(DateTimeException dte) {
return null;
}
}
Here is my incomplete Test method :
#Mock
MyClass mockMyClass;
#Test(expected = DateTimeException.class)
public void testmyMethodToTest_Exception() {
String error = "Error while parsing the effective end date";
doThrow(new DateTimeException(error)).when(--need to mock here---);
ZonedDateTime dateTime = mockMyClass.myMethodTotest();
}
I want to mock the ZonedDateTime.ofInstant() method to throw a DateTimeException while parsing for the negative scenario. How I can do that.
As of now (18/03/2022) Mockito supports mocking static methods. You can do
#Test
public void testDate() {
String instantExpected = "2022-03-14T09:33:52Z";
ZonedDateTime zonedDateTime = ZonedDateTime.parse(instantExpected);
try (MockedStatic<ZonedDateTime> mockedLocalDateTime = Mockito.mockStatic(ZonedDateTime.class)) {
mockedLocalDateTime.when(ZonedDateTime::now).thenReturn(zonedDateTime);
assertThat(yourService.getCurrentDate()).isEqualTo(zonedDateTime);
}
}
Please note that you need to use mockito-inline dependency:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.4.0</version>
</dependency>
You cannot use Mockito for this because ZonedDateTime is a final class and ofInstant is a static method, but you can use the PowerMock library to enhance Mockito capabilities:
final String error = "Error while parsing the effective end date";
// Enable static mocking for all methods of a class
mockStatic(ZonedDateTime.class);
PowerMockito.doThrow(new DateTimeException(error).when(ZonedDateTime.ofInstant(Mockito.anyObject(), Mockito.anyObject()));

How to kill a conditional boundary mutation concerning time

I'm trying to test the following class
#Component
public class StreamListener {
#Value("${kafkaMessageExpiration.maxExpirationInMilliseconds}")
private long messageExpirationTime;
public void handleMessage(Payment payment) {
logIncomingDirectDepositPayment(payment);
if (!isMessageExpired(payment.getLastPublishedDate())) {
messageProcessor.processPayment(payment);
}
}
private boolean isMessageExpired(OffsetDateTime lastPublishedDate) {
return ChronoUnit.MILLIS.between(lastPublishedDate.toInstant(), Instant.now()) > messageExpirationTime;
}
}
I'm getting a "changed conditional boundary → SURVIVED" message on the condition in isMessageExpired().
I have the following tests which test when the difference is less than messageExpirationTime and when the difference is greater than messageExpirationTime.
#BeforeEach
void init() {
ReflectionTestUtils.setField(streamListener, "messageExpirationTime", 60000);
}
#Test
void handleMessage() {
Payment payment = TestObjectBuilder.createPayment();
streamListener.handleMessage(incomingDirectDepositPayment);
verify(messageProcessor).processDirectDepositPayment(incomingDirectDepositPayment);
}
#Test
void handleMessage_expired_message() {
Payment payment = TestObjectBuilder.createPayment();
payment.setLastPublishedDate(OffsetDateTime.now(ZoneId.of("UTC")).minusMinutes(10));
streamListener.handleMessage(incomingDirectDepositPayment);
verify(messageProcessor, never()).processDirectDepositPayment(incomingDirectDepositPayment);
}
I suspect the problem is that I don't have a test where the difference is equal. Assuming that is what I'm missing, I don't know how to get the difference to be equal. Any suggestions on how I can kill this mutation?
BTW, I'm using Java 11, JUnit 5 and PITest 1.6.3
This issue is this
Instant.now()
If time is one of the inputs your code depends on you need to make that explicit so you can control it.
This is normally achieved by injecting a java.util.Clock (held as a field, injected via the constructor). Your call to Instant.now() can then be replaced with clock.instant() and the code becomes properly unit testable.

How to mock clock.millis() in Java

I have a code where I calculate the difference between current clock time and time saved in my db
If difference between the two is greater than certain value then I return result accordingly.
For this case I am trying to write test but is facing problem to mock Clock.millis(). I have referred some answers but none worked for me can someone help me with that.
For my test I want to mock this clock.millis() function with a fixed value so that each time I run test it takes that value only.
A Clock is meant for providing access to the current instant, date and time using a time-zone. You don't really need to mock it. As your class needs to obtain the current instant, so it should receive an instance of the Clock in the constructor:
public class FooService {
private final Clock clock;
public FooService(Clock clock) {
this.clock = clock;
}
public boolean isLocked() {
long differenceInSecond = (clock.millis() - this.getLockedAt()) / 1000;
return differenceInSecond < 7200;
}
private long getLockedAt() {
...
}
}
Then, in your test, you can use a fixed() clock, which will always return the same instant:
#Test
public void isLocked_shouldReturnTrue_whenDifferenceInSecondIsSmallerThan7200() {
// Create a fixed clock, which will always return the same instant
Instant instant = Instant.parse("2020-01-01T00:00:00.00Z");
Clock clock = Clock.fixed(instant, ZoneOffset.UTC);
// Create a service instance with the fixed clock
FooService fooService = new FooService(clock);
// Invoke the service method and then assert the result
boolean locked = fooService.isLocked();
assertThat(locked).isTrue();
}
In a Spring Boot application, you could expose a Clock as a #Bean:
#Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
And then Spring will take care of injecting it into your service:
#Service
public class FooService {
private final Clock clock;
public FooService(Clock clock) {
this.clock = clock;
}
...
}
You need to wrap Clockinto your own class, exposing Clock.millis() as a delegate. In your test you can then mock your wrapper and return whatever you like.

java: how to mock Calendar.getInstance()?

In my code I have something like this:
private void doSomething() {
Calendar today = Calendar.getInstance();
....
}
How can I "mock" it in my junit test to return a specific date?
You can mock it using PowerMock in combination with Mockito:
On top of your class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ClassThatCallsTheCalendar.class})
The key to success is that you have to put the class where you use Calendar in PrepareForTest instead of Calendar itself because it is a system class. (I personally had to search a lot before I found this)
Then the mocking itself:
mockStatic(Calendar.class);
when(Calendar.getInstance()).thenReturn(calendar);
As far as I see it you have three sensible options:
Inject the Calendar instance in whatever method/class you set that day in.
private void method(final Calendar cal)
{
Date today = cal.getTime();
}
Use JodaTime instead of Calendar. This is less an option and more a case of a suggestion as JodaTime will make your life a lot easier. You will still need to inject this time in to the method.
DateTime dt = new DateTime();
Date jdkDate = dt.toDate();
Wrap Calendar inside some interface that allows you to fetch the time. You then just mock that interface and get it to return a constant Date.
Date today = calendarInterfaceInstance.getCurrentDate()
Don't mock it - instead introduce a method you can mock that gets dates. Something like this:
interface Utility {
Date getDate();
}
Utilities implements Utility {
public Date getDate() {
return Calendar.getInstance().getTime();
}
}
Then you can inject this into your class or just use a helper class with a bunch of static methods with a load method for the interface:
public class AppUtil {
private static Utility util = new Utilities();
public static void load(Utility newUtil) {
this.util = newUtil;
}
public static Date getDate() {
return util.getDate();
}
}
Then in your application code:
private void doSomething() {
Date today = AppUtil.getDate();
....
}
You can then just load a mock interface in your test methods.
#Test
public void shouldDoSomethingUseful() {
Utility mockUtility = // .. create mock here
AppUtil.load(mockUtility);
// .. set up your expectations
// exercise the functionality
classUnderTest.doSomethingViaAPI();
// ... maybe assert something
}
See also Should you only mock types you own? and Test smell - everything is mocked
Using Mockito and PowerMockito:
Calendar endOfMarch = Calendar.getInstance();
endOfMarch.set(2011, Calendar.MARCH, 27);
PowerMockito.mockStatic(Calendar.class);
Mockito.when(Calendar.getInstance()).thenReturn(endOfMarch);
Refer to the link for the complete code.
Write a class called DateHelper with a method getCalendar that returns Calendar.getInstance(). Refactor the class that you're testing so that it has a member variable of type DateHelper, and a constructor that injects that member variable. Use that constructor in your test, to inject a mock of DateHelper, in which getCalendar has been stubbed to return some known date.
You can mockit using JMockit. Here you can see how you can do it: Mock Java Calendar - JMockit vs Mockito.
Avoid use Calendar.getInstance() and just use Mockito methods to return what you like.
For example:
#Test
fun italianLocale_returnsItalianFormatDate() {
val calendar: Calendar = Mockito.mock(Calendar::class.java)
Mockito.`when`(calendar.get(Calendar.DAY_OF_MONTH)).thenReturn(27)
Mockito.`when`(calendar.get(Calendar.YEAR)).thenReturn(2023)
Mockito.`when`(calendar.get(Calendar.MONTH)).thenReturn(1)
val formatted = calendar.toReadableDate()
assert(formatted == "27/01/2023")
}
import Mockito in your gradle file with:
testImplementation ("org.mockito.kotlin:mockito-kotlin:x.x.x")
or (if you are using groovy)
testImplementation "org.mockito.kotlin:mockito-kotlin:x.x.x"

Categories

Resources