Grails 在控制器测试中创建的域对象不会在数据库中结束
我一直试图在行动中遵循圣杯(GiA MEAP,第二版),但偶尔我会遇到一些即使花了很多时间也无法解决的问题。这就是其中之一。很抱歉,这个问题与GiA中的示例有关(清单6.x,第144页中的v13,ch6,PostControllerSpec),但是我将尝试用一个精简但完整的代码来捕获这个问题。我有两个域模型,Grails 在控制器测试中创建的域对象不会在数据库中结束,grails,controller,mocking,Grails,Controller,Mocking,我一直试图在行动中遵循圣杯(GiA MEAP,第二版),但偶尔我会遇到一些即使花了很多时间也无法解决的问题。这就是其中之一。很抱歉,这个问题与GiA中的示例有关(清单6.x,第144页中的v13,ch6,PostControllerSpec),但是我将尝试用一个精简但完整的代码来捕获这个问题。我有两个域模型,User和Post package grailstuts class User { String loginId static hasMany = [ posts: Pos
User
和Post
package grailstuts
class User {
String loginId
static hasMany = [ posts: Post ]
static constraints = { loginId(blank: false, unique: true) }
}
PostController
如下所示
package grailstuts
class PostController {
static scaffold = true
def timeline() {
def user = User.findByLoginId(params.id)
if (!user) {
response.sendError(404)
} else {
[ user : user ]
}
}
def addPost() {
def user = User.findByLoginId(params.id)
if (user) {
def post = new Post(params)
user.addToPosts(post)
if (user.save()) {
flash.message = "Successfully created Post"
} else {
flash.message = "Invalid or empty post"
}
} else {
flash.message = "Invalid User Id"
}
redirect(action: 'timeline', id: params.id)
}
}
当我尝试按如下方式对控制器进行单元测试时(如书中所示),我遇到了这个问题
package grailstuts
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
@TestFor(PostController)
@Mock([User,Post])
class PostControllerSpec extends Specification {
def "Adding a valid new post to the timeline"() {
given: "A user with posts in the db"
User chuck = new User(loginId: "chuck_norris").save(failOnError: true)
and: "A loginId parameter"
params.id = chuck.loginId
and: "Some content for the post"
params.content = "Chuck Norris can unit test entire applications with a single assert."
when: "addPost is invoked"
def model = controller.addPost()
then: "our flash message and redirect confirms the success"
flash.message == "Successfully created Post"
response.redirectedUrl == "/post/timeline/${chuck.loginId}"
Post.countByUser(chuck) == 1
}
}
该测试通过了前两个测试flash.message==“成功创建了Post”
和response.redirectedUrl==“Post/timeline/${chuck.loginId}”
,但在最后一行Post.countByUser(chuck)==1
(这非常令人费解,无法解释失败的原因)。我得到以下信息:
| Condition not satisfied:
Post.countByUser(chuck) == 1
| | |
0 | false
grailstuts.User : 1
我的问题是,为什么上面的测试失败了,即使它成功地创建了post?我花了相当多的时间试图找出错误,但还没有运气。在无数次尝试之后,我做了一些有效的事情。不过,我还是很困惑,如果有人能解释一下,那真的会有帮助。我为它工作所做的唯一更改是,如果
user.save()
为true
,则显式保存post
。这是修改后的addPost()
(唯一更改的行清楚地标记为//显式保存帖子!!
,其他地方没有其他更改)
注意:即使我做了if(user.save(flush:true))
而不仅仅是if(user.save())
,但没有将post显式保存为post.save(flush:true,failOnError:true)
,它也不起作用。我必须明确地保存这篇文章
既然保存用户
应该自动保存帖子
,这种行为仍然让我感到困惑。如果有人能解释这种行为,那将非常有帮助。感谢那些花时间调查此事的人
更新-- 彼得·莱德布鲁克的解释如下(): 您使用的是哪个版本的Grails?我刚刚试过Grails2.2.1 在测试中得到了一个NullPointerException。升级到2.2.4固定版本 这个特别的问题。可能是圣杯的问题 仅供参考,您甚至不需要在中显式保存用户实例 保留该帖子的操作。所以肯定是出了问题。这个 级联存储被破坏,可能是在Grails的模拟数据库中 在单元测试期间使用的实现。我猜 应用程序正常运行时工作正常(例如通过run app)
我也犯了同样的错误,我认为问题在于
def post = new Post(params)
正如它所说
params.id: "chuck_norris"
到后期创作,但奇怪的是不是params.content
我通过将线路改为
def post = new Post(content: params.content)
在控制器中保存用户时,您是否也尝试过刷新<代码>用户。保存(刷新:真,失败者错误:真)?使用
failOnError
保存时,还要查看它是否失败。谢谢,@dmahapatro。事实上,是的,我也试过。也就是说,如果我将if(user.save())
替换为if(user.save(flush:true,failOnError:true))
中的addPost()
,它不会失败。相反,前两个测试仍然通过,只有在第三行Post.countByUser(chuck)==1时失败(与我原来的问题完全相同)。但是,如果在保存后我println post.id
,它会给出null
(这是一个提示,但我仍然不明白它如何能够保存用户而不会失败,但我无法在数据库中找到帖子)。你确定loginId
是唯一的吗?我想是的,因为我已经指定了约束loginId(blank:false,unique:true)
,数据库中还没有其他用户。但是,我可能遗漏了什么(我是Grails的新手),因此,如果您能详细说明您的确切意思,我可以试一试。谢谢!可能是因为您在测试中的params
中有id
。只需将文章内容直接保存为new Post(内容:params.content)
。如果可能,将params.id
更改为params.loginId
,则前者具有误导性,不公平。
params.id: "chuck_norris"
def post = new Post(content: params.content)