14

My spring-data-jpa backend has a class that populates the (test) database with a lot of test data. The class usese the spring data repositories to create entities. All my entities have a field annotated with @CreatedData and the corresponding @EntityListeners(AuditingEntityListener.class) annotation on the model class. This works fine so far. dateCreated is automatically set correctly.

But when running Junit test I sometimes need to create a (test) object with a dateCreated in the past. How can I archive this? Only via plain JDBC?

Robert
  • 1,579
  • 1
  • 21
  • 36

4 Answers4

7

In case you are using Spring Boot, you can mock dateTimeProvider bean used in EnableJpaAuditing annotation. Spring Data uses this bean to obtain current time at the entity creation/modification.

@Import({TestDateTimeProvider.class})
@DataJpaTest
@EnableJpaAuditing(dateTimeProviderRef = "testDateTimeProvider")
public class SomeTest {

    @MockBean
    DateTimeProvider dateTimeProvider;

...

It is necessary to define actual testDateTimeProvider bean, but it won't be used at all, as you will use mock instead. You can write mockito methods afterwards as usual:

@Test
public void shouldUseMockDate() {

    when(dateTimeProvider.getNow()).thenReturn(Optional.of(LocalDateTime.of(2020, 2, 2, 0, 0, 0)));
    
   
   ... actual test assertions ...
6

I found a way that works for me (using plain JDBC):

First I create my domain objects for testing with spring-data-jpa:

MyModel savedModel = myRepo.save(myModel);

That automatically fills the "dateCreated" with timestamp of "now". Since I need creation dates in the past for testing I manually tweak them with plain JDBC:

@Autowired
JdbcTemplate jdbcTemplate;

[...]

// This syntax is for H2 DB.  For MySQL you need to use DATE_ADD
String sql = "UPDATE myTable SET created_at = DATEADD('DAY', -"+ageInDays+", NOW()) WHERE id='"+savedLaw.getId()+"'";
jdbcTemplate.execute(sql);
savedModel.setCreatedAt(new Date(System.currentTimeMillis() - ageInDays* 3600*24*1000);

Do not forget to also setCreatedAt inside the returned model class.

Robert
  • 1,579
  • 1
  • 21
  • 36
2

The best and cleanest way I found using Mockito is to change the AuditingHandler before saving by sating your worn implementation of DateTimeProvider. Since Java 8 this can be done using Lambda Interfaces like this:

@SpyBean
private AuditingHandler auditingHandler;

// inside the test
auditingHandler.setDateTimeProvider(() -> Optional.of(YOUR_DATE));
savedEntity = repository.save(entity);
user1928596
  • 1,503
  • 16
  • 21
0

I do not know exact test case which you are using, but i can see few solutions:

  • create a mock object, once the dateCreated is called return date month ago
  • maybe use in-mem db, populate it with date before test
  • go with AOP from the link provided in comments
pezetem
  • 2,503
  • 2
  • 20
  • 38
  • Thank you for the suggestions: MockObjects: I need hundrests entries each with different creation dates in my DB. InMemoryDB: That is what I am using. I populate it with my TestDataCreator.java I am creating my testdata in java (instead of plain sql in a data.sql file) because I need to juggle a lot of references between the test data entries. – Robert Feb 21 '17 at 18:27