Main Tutorials

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

pom.xml

   <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.

ExampleEntity.java

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.

JpaConfig.java

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

ExampleRepository.java

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.

ExampleServiceTest.java

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.

ExampleServiceTest.java

  @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

References

About Author

author image
Halit Talha TÜRE is a senior software development engineer with more than 6 years of experience. He is a Phd candidate and an open-source contributor.

Comments

Subscribe
Notify of
1 Comment
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
faouzi chabchoub
3 years ago

thanks for your help