Mocking Spring Data DateTimeProvider
In this article, we will demonstrate an integration test where we have to persist the entities with mocked auditing date fields when JPA Auditing is enabled.
Technologies used:
- Spring Boot 2.4.0
- Spring 5.3.1
- JUnit Jupiter 5.7.0
- Tomcat embed 9.0.39
- Java 8
1. Project Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. Spring Data Audited Entity
Below is an audited entity class. The @CreatedDate
and @LastModifiedDate
will generate the created and modified times automatically.
package com.talhature.tutorial.datetimeprovidermock.model;
import java.time.LocalDateTime;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Entity
@Table
@EntityListeners(AuditingEntityListener.class)
public class ExampleEntity {
@Id
@GeneratedValue
private long id;
@Version
private int version;
@CreatedDate
private LocalDateTime createdDateTime;
@LastModifiedDate
private LocalDateTime updateDateTime;
private String description;
// getters, setters, hashCode, equals and etc...
}
The @EnableJpaAuditing
enables the JPA Auditing.
package com.talhature.tutorial.datetimeprovidermock.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
3. Spring Data Repository
package com.talhature.tutorial.datetimeprovidermock.repository;
import java.time.LocalDateTime;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.talhature.tutorial.datetimeprovidermock.model.ExampleEntity;
public interface ExampleRepository extends JpaRepository<ExampleEntity, Long> {
List<ExampleEntity> findByCreatedDateTime(LocalDateTime createdDateTime);
List<ExampleEntity> findByUpdateDateTime(LocalDateTime createdDateTime);
}
4. Mocking DateTimeProvider
If JPA auditing is enabled for an entity, the entity’s creation and modification dates are set by Spring Data’s AuditingHandler
. The AuditingHandler
queries the current date-time from a DateTimeProvider
instance whose life cycle is not managed by the Spring context. That’s why having a MockBean of DateTimeProvider
will not work on its own.
In our test class, we have to set the dateTimeProvider
field of AuditingHandler
with our mocked bean.
package com.talhature.tutorial.datetimeprovidermock.service;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import com.talhature.tutorial.datetimeprovidermock.model.ExampleEntity;
@SpringBootTest
@AutoConfigureTestDatabase
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
class ExampleServiceTest {
@Autowired
private ExampleService service;
@MockBean
private DateTimeProvider dateTimeProvider;
@SpyBean
private AuditingHandler handler;
@BeforeEach
void setUp() throws Exception {
MockitoAnnotations.openMocks(this);
handler.setDateTimeProvider(dateTimeProvider);
}
//...
}
Now we can mock the dateTimeProvider.getNow()
call and persist our entities.
@Test
void exampleServiceTest_getExampleByCreatedDate_OK() {
ExampleEntity example = new ExampleEntity();
example.setDescription("simple description");
LocalDateTime createdDateTime = createLocalDateTime("2020-10-17 00:00");
when(dateTimeProvider.getNow()).thenReturn(Optional.of(createdDateTime));
service.saveExample(example);
List<ExampleEntity> selectedList = service.findByCreatedDateTime(createdDateTime);
assertTrue(selectedList.stream().allMatch(item -> createdDateTime.equals(item.getCreatedDateTime())));
}
@Test
void exampleServiceTest_getExampleByUpdateDateTime_OK() {
ExampleEntity example = new ExampleEntity();
example.setDescription("simple description");
LocalDateTime createdDateTime = createLocalDateTime("2020-10-17 00:00");
when(dateTimeProvider.getNow()).thenReturn(Optional.of(createdDateTime));
service.saveExample(example);
LocalDateTime updateDateTime = createLocalDateTime("2099-11-23 05:00");
when(dateTimeProvider.getNow()).thenReturn(Optional.of(updateDateTime));
example.setDescription("changed description");
service.saveExample(example);
List<ExampleEntity> selectedList = service.findByUpdateDateTime(updateDateTime);
assertTrue(selectedList.stream().allMatch(item -> updateDateTime.equals(item.getUpdateDateTime())));
}
private LocalDateTime createLocalDateTime(String date) {
return LocalDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
}
Download Source Code
$ git clone https://github.com/HalitTalha/datetimeprovider-mock.git
$ mvn spring-boot:run
References
- Spring Data – Auditing
- Spring Data – AuditingHandler
- Spring Data – AuditingHandlerSupport
- Spring Data – DateTimeProvider
- Spring Boot – AutoConfigureTestDatabase
- Spring Boot – Integration Test Example
- Spring Boot – Spring data JPA
- Spring Boot – Auditing with JPA, Hibernate and Spring Data JPA
- Spring Boot – A Quick Guide to @DirtiesContext
thanks for your help