2
votes

I recently created a new controller in a pretty big app, (note: we just upgraded from Grails 2.0.3 to 2.3.7) and the created tests have all been failing inexplicably. Most of my errors consist of the model being [:] after a controller call. And therefore any methods on that model being null. I get stuff like:

groovy.lang.MissingMethodException: 
No signature of method: 
TransitionController.save() is applicable for argument types: (Transition) 
values: [null] Possible solutions: save(), wait(), show(), any(), 
wait(long), raw(java.lang.Object) at TransitionControllerSpec.Test the 
save action correctly persists an instance(TransitionControllerSpec.groovy:45)

I have tried explicitly assigning the model by doing:

def model = controller.delete()
def model = controller.update()
//....

But I get the same result, an empty model map and null values if I try to access it. As per this article (https://jira.grails.org/browse/GRAILS-8462) I could also try accessing the model by doing:

def model = controller.modelAndView.model 

But this did not work for me either, producing the same results.

Any ideas on what might be happening?

Edit: Here are the first couple of tests

package com.hey.how.are.ya

import grails.test.mixin.*
import spock.lang.*
import org.junit.*

@TestFor(TransitionController)
@Mock(Transition)
class TransitionControllerSpec extends Specification {

def populateValidParams(params) {
    assert params != null
    params['reason'] = 'just cuz'
}

void "Test the index action returns the correct model"() {

    when:"The index action is executed"
        controller.index()

    then:"The model is correct"
        !model.transitionInstanceList
        model.transitionInstanceCount == 0
}

void "Test the create action returns the correct model"() {
    when:"The create action is executed"
        controller.create()

    then:"The model is correctly created"
        model.transitionInstance!= null
}
//...grails generated tests here

}

Edit: Here's an interesting case! If I do:

void "Test the create action returns the correct model"() {
    when:"The create action is executed"
        def model = controller.create()
        def inst = new Transition()
        println "${model}"
        println "${model.transitionInstance}"
    then:"The model is correctly created"
        model.transitionInstance != null
}

Then I get this as output:

[transitionInstance:null]
null

But the test passes. This only happens with create. What is going on??

Edit: Adding code for create and save

def create() {
    [transitionInstance: new Transition(params)]
}

def save() {
    def transitionInstance = new Transition(params)
    if (!transitionInstance.save(flush: true)) {
        render(view: "create", model: transitionInstance: transitionInstance])
        return
    }

    flash.message = message(code: 'default.created.message', args: [message(code: 'transition.label', default: 'Transition'), transitionInstance.id])
    redirect(action: "show", id: transitionInstance.id)
}

Edit: I can't spend too much time on this, but I'm pretty sure the problem is discrepancies between the controller generated and the test created. For what it's worth I was running version 2.0.2 of the scaffolding plugin and grails 2.3.7. I'm gonna throw out the tests created by the command and start from scratch, thanks for the help!

1
could you show a whole test? normally, if you use the @TestFor Annotation you'll get the model and view variable from the mixin. - Mario David
I added the first couple of tests and my imports. I agree that the test for annotation should get me the model. - janDro
Can you add the test for the save method as well as the save method itself to the post? because the error message does not correspond to the tests you showed. - Mario David
I'm having the same problem on v2.4.4. - Pablo Pazos

1 Answers

0
votes

I had the same issue but solved it like following:

Inside the controller method:

 render view: "_myTemplate", model: [instance: myInstance]

So the controller, still renders the template and inside the test spec I can test

assert(model.instance = myExpectedInstance)