3

I've got a sample Domain such as this

class User {
    String username
    String password

    def userHelper

    static contraints = {
        username(nullable: false, blank: false)
        password nullable: false, blank: false, validator: {pwd, userInstance ->
            return userInstance.userHelper.validatePassword(pwd)
        }
    }
}

the userHelper is being injected by the following in my resources.groovy

beans = {
    userHelper(UserHelper)
}

When I test my application from the browser, everything runs fine. However, I get an exception while trying to write functional tests for this.

The error says: Cannot invoke method validatePassword() on null object

So I'm assuming that userHelper is not being set when I run my functional test case.

My test case looks like this:

 @TestFor(UserController)
 @Mock([User])

class UserControllerSpecification extends Specification {


    def "save user" () {
        given:
            request.contentType = "application/json"
            request.JSON = """
                            {user:
                                {
                                    username: "somtething"
                                    password: "something"
                                }
                            }
                        """
        when: 
            controller.save()

        then:
            User.count() == 1
    }
}

Update

Controller:

class UserController {
    def userService
    def save() {
        def user = new User(params?.user)
        request.withFormat {
            json {
                if(user.validate())
                  userService.processUser()
                  //do something
                else
                  //do something else
            }
        }
    }
}

Questions

  • How can I set the userHelper property on User domain prior to running my tests?
  • How can I run my code in Bootstrap.groovy prior to running all my functional and integration test cases?
Anthony
  • 33,838
  • 42
  • 169
  • 278
  • Did you mean `return userInstance.userHelper.validatePassword(pwd)`, or `return userHelper.validatePassword(pwd)`? – tim_yates Jun 18 '13 at 15:11
  • I meant `return userInstance.userHelper.validatePassword(pwd)` as I understand, the second parameter to the `validation` is the domain class instance being validated – Anthony Jun 18 '13 at 15:14

2 Answers2

4

Have you tried using defineBeans? Since this is a unit test you have to explicitly define the beans. This how it is normally done in JUnit

defineBeans{
    userHelper(UserHelper){bean ->
        //in case you have other beans defined inside helper
        bean.autowire = true 
    }
}

You can defineBeans in the setup method to make it available for all the test cases.

@Before
void setup(){
    defineBeans{
        userHelper(UserHelper){bean ->
            //in case you have other beans defined inside helper
            bean.autowire = true 
        }
    }
}   

UPDATE:

Seems like in case of spock you can directly inject a spring bean inside a Unit Test (test which extends Specification)

class UserControllerSpecification extends Specification {
    def userHelper //injected here in case of spock

    def "save user" () {
     ................
    }
}
dmahapatro
  • 49,365
  • 7
  • 88
  • 117
  • Hmm that didn't work. I added the `defineBeans` in the `given` section – Anthony Jun 18 '13 at 15:41
  • unfortunately, this isn't working for me. I keep getting the same error that validatePassword cant be invoked on null object. I'm running my test cases like this `grails test-app unit:spock` I tried putting it in the functional folder as well but no luck (ran with `grails test-app functional:spock`) – Anthony Jun 18 '13 at 15:50
  • @Anthony I think you can directly inject the bean in a spock unit test. Have a look at the update and [this question](http://stackoverflow.com/a/11953052/2051952) as well. – dmahapatro Jun 18 '13 at 15:51
  • Can you have `defineBeans` in [`setup:`](https://code.google.com/p/spock/wiki/SpockBasics#Blocks) instead of `given:` – dmahapatro Jun 18 '13 at 19:03
  • @dmahapatro the answer you are referring to is an integration test, not a unit test! That is why they can inject a bean into the test. – Luis Muñiz Mar 09 '15 at 17:39
1

Try using the metaClass.

def setup(){
    User.metaClass.getUserHelper = {new UserHelper()}
}

You could also mock userHelper if that's what you want.

def setup(){
    def userHelperMock = Mock(UserHelper)
    userHelperMock.validatePassword(_) >> true
    User.metaClass.getUserHelper = {userHelperMock}
}

Make sure you are calling getUserHelper() and not accessing the private userHelper property directly. It might be a good idea to make that explicit:

password nullable: false, blank: false, validator: {pwd, userInstance ->
    return this.getUserHelper().validatePassword(pwd)
}

@dmahapatro 's answer to use defineBeans{...} is the best way, but I think it's not working because of this grails bug...

The other suggestion to directly inject the bean into the test will only work in an integration test that extends grails.plugin.spock.IntegrationSpec.

Becca Gaspard
  • 1,165
  • 7
  • 19