2
votes

I'm attempting to test a typical controller flow for user login. There are extended relations, as with most login systems, and the Grails documentation is completely useless. It doesn't have a single example that is actually real-world relevant for typical usage and is a feature complete example.

my test looks like this:

@TestFor(UserController)
class UserControllerTests extends GroovyTestCase {
    void testLogin() {
        params.login = [email: "[email protected]", password: "123"]
        controller.login()
        assert "/user/main" == response.redirectUrl
    }
}

The controller does:

def login() {
    if (!params.login) {
        return
    }
    println("Email " + params.login.email)
    Person p = Person.findByEmail(params?.login?.email)
    ...
}

which fails with:

groovy.lang.MissingMethodException: No signature of method: immigration.Person.methodMissing() is applicable for argument types: () values: []

The correct data is shown in the println, but the method fails to be called.

The test suite cannot use mocks overall because there are database triggers that get called, the result of which will need to be tested.

The bizarre thing is that if I call the method directly in the test, it works, but calling it in the controller doesn't.

For an update: I regenerated the test directly from the grails command, and it added the @Test annotation:

    @TestFor(UserController)
    class UserControllerTests extends GroovyTestCase {
        @Test
        void testLogin() {
            params.login = [email: "[email protected]", password: "123"]
            Person.findByEmail(params.login.email)
            controller.login()
        }
    }

This works if I run it with grail test-app -integration UserController

though the result isn't populated correctly - the response is empty, flash.message is null even though it should have a value, redirectedUrl is null, content body is empty, and so is view.

If I remove the @TestFor annotation, it doesn't work even in the slightest. It fails telling me that 'params' doesn't exist.

In another test, I have two methods. The first method runs, finds Person.findAllByEmail(), then the second method runs and can't find Person.findAllByEmail and crashes with a similar error - method missing.

In another weird update - it looks like the response object is sending back a redirect, but to the application baseUrl, not to the user controller at all.

2

2 Answers

6
votes

Integration tests shouldn't use @TestFor. You need to create an instance of the controller in your test, and set params in that:

class UserControllerTests extends GroovyTestCase {
  void testLogin() {
    def controller = new UserController()
    controller.params.login = [email:'[email protected]', password:'123']
    controller.login()
    assert "/user/main" == controller.response.redirectedUrl
  }
}

Details are in the user guide.

0
votes

The TestFor annotation is used only in unit tests, since this mocks the structure. In the integration tests you have access of the full Grails environment, so there's no need for this mocks. So just remove the annotation and should work.

class UserControllerTests extends GroovyTestCase {
...
}