Mxb
Mxb

Reputation: 101

grails unit testing with multiple datasource

I am trying to write unit test case for grails controller which has following structure:

class MyController{

    def save(){
         def myDomain = new MyDomain(params)
         business validation 1
         business validation 2
         myDomain.writedatasource.save()
         business validation 3
         business validation 4
    }
}

Since Unit test doesn't load Datasource.groovy the writedatasource isn't available during unit testing so the test cases for "business validation 3" and "business validation 4" fail as I get

groovy.lang.MissingPropertyException: No such property: writedatasource for class: MyDomain

How can I modify my test case to test validation scenarios 3 and 4?

Test case is simple and looks like follows:

void testSave(){
    ...setup...
    controller.save()
    assert conditions
    ....

}

Upvotes: 1

Views: 1135

Answers (2)

Teddy Yueh
Teddy Yueh

Reputation: 674

Addressing original question:

I ran into this and would have had to mock many, many more methods than just 'save' on multiple domains. So instead, I overrode the getter of my domains to simply return the invoking domain instance:

def setup() {
    [MyDomain1, MyDomain2].each { Class clazz ->
        mockDomain(clazz)
        clazz.metaClass.getWritedatasource = { 
            return delegate
        }
        clazz.metaClass.'static'.getWritedatasource = {
            return delegate
        }
    }
}

This also saves me from including @DirtiesRuntime since I'm not updating the metaClass of anything I'd want to clean.

Most importantly, whatever calls the datasource, be it the domain class or the instance, it should be decorated with GORM fanciness from mockDomain, meaning I don't have to mock any of the GORM methods.

What if you want dynamic datasources?

In my particular case, datasources are configurable and may change depending on environment. If you're in a similar situation, you can configure this in your domain mapping:

static mapping = {
    datasources    Holders.grailsApplication?.config.dynamic?.datasources
    ...
}

where dynamic.datasources is an array of datasource names. Then, in the test setup:

def setup() {
    grailsApplication.config.dynamic.datasources = ['foo', 'bar']
    [MyDomain1, MyDomain2].each { Class clazz ->
        mockDomain(clazz)
        grailsApplication.config.dynamic.datasources.each{
            clazz.metaClass."get${it.capitalize()}" = { 
                return delegate
            }
            clazz.metaClass.'static'."get${it.capitalize()}" = { 
                return delegate
            }
        }
    }
}

Upvotes: 1

Fran García
Fran García

Reputation: 2050

Not sure if that could make the trick, but you can try:

def 'the controller call the save method in writedatasource'() {
    given:
        def calls = 0
    and:
        def myDomainInstance = new MyDomain()
    and:
        MyDomain.metaClass.getWritedatasource = { new Object() }
    and:
        Object.metaClass.save = { Map attrs ->
            calls++
        }
    when:
        controller.save()
    then:
        calls == 1
}

But the only thing are you doing is testing that the save is called under writedatasource, so it would be better to have also an integration test. If that makes the trick, please answer as well at http://grails.1312388.n4.nabble.com/Mocking-in-a-unit-test-a-domain-object-multiple-datasources-td4646054.html as they seem to have the same problem.

Upvotes: 1

Related Questions