Nikolas
Nikolas

Reputation: 44398

How to fake last modified time for unit tests?

I have a simple entity with a requirement that last modified time should be updated on persist.

@Data      // Lombok thing
@Entity
@Table(name = "MY_ENTITY")
public class MyEntity {

    @Column(name = "LAST_MODIFIED", nullable = false)
    private LocalDateTime lastModified;

    // irrelevant columns including id omitted

    @PrePersist
    public void initializeUUID() {
        lastModified = LocalDateTime.now();
    }
}

I have a requirement to implement a job that queries such entities older than a certain time (let's say a day), modifies its state and persists them. I have a problem with data creation for an unit test that covers such use case.

Although I set manually lastModified time, the @PrePersist causes its change regardless the set value.

@Autowired  // Spring Boot tests are configured against in-memory H2 database
MyEntityRepository myEntityRepository;
var entity = new MyEntity();
entity.setLastModified(LocalDateTime.now().minusDays(3));
myEntityRepository.entity(entity);

Question: How to prepare pre-persisted data (lastModified) without drastically modifying the MyEntity class just for sake of unit tests? A solution using Mockito is welcome.

Note I use Spring Boot + jUnit 5 + Mockito

Things I have tried:

Upvotes: 3

Views: 3740

Answers (2)

Roger Gustavsson
Roger Gustavsson

Reputation: 1709

With Mockito you could perhaps do something like this?

MyEntity sut = Mockito.spy(new MyEntity());

Mockito.doNothing().when(sut).initializeUUID();

But I'm not sure exactly where it would fit in your tests.


Two other options

  1. Mock LocalDateTime.now() and let it return the value you want. But maybe other code in the persist process calls this method and may not like it. If that is the case, over to the other option
  2. Wrap LocalDateTime.now() in your own class with a static method and mock that instead. Sadly involves minor changes to your entity class, but only that call to LocalDateTime.now() will be mocked.

I haven't described how to mock with Mockito in this case because I'm not familiar with it. I've only used JMockit. But the above would be the principle.

Upvotes: 0

wjans
wjans

Reputation: 10115

You could use the Spring Data JPA AuditingEntityListener.

Simply enable it via @org.springframework.data.jpa.repository.config.EnableJpaAuditing and optionally provide a custom dateTimeProviderRef like this:

@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "myAuditingDateTimeProvider")
public class JpaAuditingConfig {

    @Bean(name = "myAuditingDateTimeProvider")
    public DateTimeProvider dateTimeProvider(Clock clock) {
        return () -> Optional.of(now(clock));
    }
}

Your entity could look something like this then:

@Data      // Lombok thing
@Entity
@Table(name = "MY_ENTITY")
@EntityListeners(AuditingEntityListener.class)
public class MyEntity {

    @LastModifiedDate
    private LocalDateTime lastModified;

    // irrelevant columns including id omitted
}

In the above example a java.time.Clock can be provided via Spring which could already solve your question regarding testing. But you could also provide a dedicated test config specifying a different/mocked DateTimeProvider.

Please note that the mentioned solution here is not a pure unit test approach. But based on your question and the things you've tried, I concluded that a solution using Spring would be feasible.

Upvotes: 3

Related Questions