Testing mock with Clock object for LocalDateTime now

 LocalDateTime.now() will create problem when unit testing, for this purpose using help of Clock object is recommended way.

Let's see example:


@Service
@RequiredArgsConstructor
@Slf4j
public class OrdersServiceImpl implements OrdersService {

private final OrdersRepository ordersRepository;
private final OrdersDeliveryEventsRepository ordersDeliveryEventsRepository;
private final MailService mailService;
private final Clock clock = Clock.systemDefaultZone();
private final FileGenerator fileGenerator;

@Override
@Scheduled(cron = "${cron.expression}")
@SchedulerLock(name = "TaskScheduler_processOrders",
lockAtMostFor = "15M", lockAtLeastFor = "2M")
public void processOrders() throws IOException {
log.info("processOrders started");
List<OrdersEntity> unsentOrders = ordersRepository
.getUnsentOrders(LocalDateTime.now(clock).minusDays(7), LocalDateTime.now(clock));
if (unsentOrders.isEmpty()) {
log.warn("There are not any orders for now");
return;
}
Firstly we create Clock object with sysemdefaultZone and then pass this
argument to LocalDateTime.now(clock) in service class; 

Testing for this:
We have to mock Clock.systemDefaultZone(); that is static method calling
via directly mentioned class,
but Mockito static methods mocking
way is different from non/static ones.
Firstly we have to add 2 dependencies:
testCompile group: 'org.mockito', name: 'mockito-inline',version: '3.6.0'
testCompile group: 'org.mockito', name: 'mockito-core', version: '4.3.1'
in our test method will be in this form:

@ExtendWith(MockitoExtension.class)
class OrdersServiceImplTest {

static Clock clock;
private static MockedStatic<Clock> mockedClock;
@Mock
private OrdersRepository ordersRepository;
@Mock
private OrdersDeliveryEventsRepository ordersDeliveryEventsRepository;
@Mock
private MailService mailService;

@Mock
private FileGenerator fileGenerator;

@InjectMocks
private OrdersServiceImpl ordersService;

@BeforeAll
static void setupClock() {
LocalDateTime localdatetime =
LocalDateTime.of(2022, 6, 7, 16, 4, 5);
clock = Clock.fixed(localdatetime.atZone(ZoneId.systemDefault()).toInstant(),
ZoneId.systemDefault());

mockedClock = mockStatic(Clock.class);
mockedClock.when(Clock::systemDefaultZone).thenReturn(clock);
}

@AfterAll
static void close() {
mockedClock.close();
}

@Test
void processOrders() throws IOException {
var ordersEntity = new OrdersEntity();
ordersEntity.setId(1L);
ordersEntity.setFullName("Test Testov");
ordersEntity.setEmail("test@test.com");

var mailResponse = new MailResponse();
mailResponse.setStatus(DeliveryStatus.SENT);
mailResponse.setMail(new MailContent());

when(ordersRepository.getUnsentOrders(LocalDateTime.now(clock).minusDays(7),
LocalDateTime.now(clock)));
}
}
Setup clock with MockedStatic object is necessary and after all tests
executed we must close this object, otherwise it will occupy
the current thread and we cann't create again same of this object in other
classes.

Comments

Popular posts from this blog

Installation instructions for some programs on linux ubuntu

Hibernate and Application Performance

timezone ile bagli Jackson deserializableda problem