How to Circumvent @EnableMongoAuditing for Custom Behavior

I recently had to write a test where I needed to manually set the value of a field annotated with @LastModifiedDate.

This proved difficult, as after much searching, I found that there was no way to selectively disable @EnableMongoAuditing once it had been enabled as part of some configuration. 

However, it appears that there is a loophole in the auditing behavior: if you perform a direct update query, you can successfully set your own custom values. It seems that this direct Mongo-level operation is beyond the scope of Spring’s auditing, and thus it can be used in exceptional situations where different behavior is required.

Furthermore, since this workaround does not require any configuration changes, it can be used in production code as well!


Here's some test code that demonstrates this behavior. In it, you can see that the values supplied on a normal insert and save are overwritten by the auditor, but the update query is able to successfully circumvent it.


@DataMongoTest
public class MongoAuditingBypassTest {

    @Configuration
    @EnableAutoConfiguration
    @EnableMongoAuditing
    public static class Config {}

    @Data
    @Document("my_document")
    public static class MyDocument {
        @Id
        private String id;

        private String name;

        @LastModifiedDate
        private Instant lastModified;
    }

    @Autowired
    MongoOperations mongoOperations;

    @BeforeEach
    @AfterEach

    void clearDb() {
        mongoOperations.remove(new Query(), MyDocument.class);
    }

    @Test

    public void mongoAuditingDemonstration() {
        MyDocument myDocument = new MyDocument();
        myDocument.setName("My Document!");
        myDocument.setLastModified(Instant.EPOCH);

        MyDocument inserted = mongoOperations.insert(myDocument);
        assertEquals(myDocument.getName(), inserted.getName());
        assertThat(inserted.getLastModified())
                .isNotEqualTo(Instant.EPOCH)
                .isCloseTo(Instant.now(), within(5L, ChronoUnit.SECONDS));

        // try doing a normal save of the record with a new date

        inserted.setLastModified(Instant.EPOCH);
        MyDocument updated = mongoOperations.save(inserted);
        assertThat(updated.getLastModified())
                .isNotEqualTo(Instant.EPOCH)
                .isCloseTo(Instant.now(), within(5L, ChronoUnit.SECONDS));

        // custom update query

        MyDocument manuallyUpdated =
                mongoOperations.findAndModify(new Query(Criteria.where("id").is(inserted.getId())),

                        new Update().set("lastModified", Instant.EPOCH),
                        FindAndModifyOptions.options().returnNew(true),
                        MyDocument.class);
        assertEquals(Instant.EPOCH, manuallyUpdated.getLastModified());
    }
}