Overriding dateCreated for testing in Grails

后端 未结 10 1395
日久生厌
日久生厌 2021-02-12 12:27

Is there any way I can override the value of dateCreated field in my domain class without turning off auto timestamping?

I need to test controller and I ha

相关标签:
10条回答
  • 2021-02-12 13:08

    As of grails 2.5.1, getMapping() method of GrailsDomainBinder class is not static,non of the above method works as is. However, @Volt0's method works with minor tweaking. Since all of us are trying to do so to make our tests working, instead of placing it in BootStrap, I placed it in actual integration test. Here is my tweak to Volt0's method:

    def disableAutoTimestamp(Class domainClass) {
        Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
        mapping.autoTimestamp = false
    }
    
    def enableAutoTimestamp(Class domainClass) {
        Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
        mapping.autoTimestamp = true
    }
    

    And simply call these methods in tests like

    disableAutoTimestamp(Domain.class)
    //Your DB calls
    enableAutoTimestamp(Domain.class)
    

    The above code can also be placed in src directory and can be called in tests however I placed this in actual test as there was only one class in my app where I needed this.

    0 讨论(0)
  • 2021-02-12 13:09

    A simpler solution is to use a SQL query in your integration test to set it as you please after you initialize your object with the other values you want.

    YourDomainClass.executeUpdate(
    """UPDATE YourDomainClass SET dateCreated = :date
    WHERE yourColumn = :something""",
    [date:yourDate, something: yourThing])
    
    0 讨论(0)
  • 2021-02-12 13:10

    The easy solution is to add a mapping:

    static mapping = {
        cache true
        autoTimestamp false
    }
    
    0 讨论(0)
  • 2021-02-12 13:15

    I couldn't get the above techniques to work, the call to GrailsDomainBinder.getMapping always returned null???

    However...

    You can use the fixtures plugin to set the dateCreated property on a domain instance

    The initial loading will not do it...

    fixture {
        // saves to db, but date is set as current date :(
        tryDate( SomeDomain, dateCreated: Date.parse( 'yyyy-MM-dd', '2011-12-25') )
    }
    

    but if you follow up with a post handler

    post {
        // updates the date in the database :D
        tryDate.dateCreated = Date.parse( 'yyyy-MM-dd', '2011-12-01')
    }
    

    Relevant part of the fixtures docs here

    AFAIK fixtures don't work for unit testing, although the plugin authors may add unit testing support in the future.

    0 讨论(0)
  • 2021-02-12 13:16

    I was having a similar issue, and was able to overwrite dateCreated for my domain (in a Quartz Job test, so no @TestFor annotation on the Spec, Grails 2.1.0) by

    • Using the BuildTestData plugin (which we use regularly anyway, it is fantastic)
    • Double-tapping the domain instance with save(flush:true)

    For reference, my test:

    import grails.buildtestdata.mixin.Build
    import spock.lang.Specification
    import groovy.time.TimeCategory
    
    @Build([MyDomain])
    class MyJobSpec extends Specification {
    
        MyJob job
    
        def setup() {
            job = new MyJob()
        }
    
        void "test execute fires my service"() {
            given: 'mock service'
                MyService myService = Mock()
                job.myService = myService
    
            and: 'the domains required to fire the job'
                Date fortyMinutesAgo
                use(TimeCategory) {
                    fortyMinutesAgo = 40.minutes.ago
                }
    
                MyDomain myDomain = MyDomain.build(stringProperty: 'value')
                myDomain.save(flush: true) // save once, let it write dateCreated as it pleases
                myDomain.dateCreated = fortyMinutesAgo
                myDomain.save(flush: true) // on the double tap we can now persist dateCreated changes
    
            when: 'job is executed'
                job.execute()
    
            then: 'my service should be called'
                1 * myService.someMethod()
        }
    }
    
    0 讨论(0)
  • 2021-02-12 13:19

    I got this working by simply setting the field. The trick was to do that after the domain object has been saved first. I assume that the dateCreated timestamp is set on save and not on object creation.

    Something along these lines

    class Message {
      String content
      Date dateCreated
    }
    
    // ... and in test class
    
    def yesterday = new Date() - 1
    def m = new Message( content: 'hello world' )
    m.save( flush: true )
    m.dateCreated = yesterday
    m.save( flush: true )
    

    Using Grails 2.3.6

    0 讨论(0)
提交回复
热议问题