问题
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
userHelperproperty onUserdomain prior to running my tests? - How can I run my code in
Bootstrap.groovyprior to running all my functional and integration test cases?
回答1:
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" () {
................
}
}
回答2:
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.
来源:https://stackoverflow.com/questions/17172482/how-to-set-objects-while-functional-testing-grails-with-spock