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