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?
4

2 回答 2

4

你试过使用defineBeans吗?由于这是一个单元测试,您必须明确定义 bean。这通常在 JUnit 中是如何完成的

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

您可以defineBeans在 setup 方法中使其可用于所有测试用例。

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

更新:

似乎在 spock 的情况下,您可以直接在单元测试中注入一个 spring bean(扩展的测试Specification

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

    def "save user" () {
     ................
    }
}
于 2013-06-18T15:34:07.553 回答
1

尝试使用元类。

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

如果这是你想要的,你也可以模拟 userHelper。

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

确保您正在调用getUserHelper()不是直接访问私有userHelper财产。明确这一点可能是个好主意:

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

@dmahapatro 的使用答案defineBeans{...}最好的方法,但我认为它不起作用,因为这个 grails 错误......

将 bean 直接注入测试的另一个建议仅适用于扩展的集成测试grails.plugin.spock.IntegrationSpec

于 2013-06-20T03:09:22.607 回答