我一直在尝试遵循Grails in Action(GiA MEAP,第2版),但我偶尔偶然发现一些即使花了很多时间也无法弄清楚的问题。这是其中之一。抱歉,这个问题与GiA中的示例相关(v13,ch6,清单6.x中的PostControllerSpec,第144页),但我将尝试使用精简但完整的代码来捕获问题。我有两个域模型,User
和Post
。
package grailstuts
class User {
String loginId
static hasMany = [ posts: Post ]
static constraints = { loginId(blank: false, unique: true) }
}
package grailstuts
class Post {
String content
static belongsTo = [ user: User]
static constraints = { content(blank: false) }
}
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 == "Successfully created Post"
和response.redirectedUrl == "/post/timeline/${chuck.loginId}"
,但在最后一行Post.countByUser(chuck) == 1
失败(这非常令人费解,无法弄清楚为什么会失败)。我得到以下内容:
| Condition not satisfied:
Post.countByUser(chuck) == 1
| | |
0 | false
grailstuts.User : 1
我的问题是为什么上述测试失败了,即使它成功创建了帖子?我花了很多时间试图找出错误,但还没有运气。
答案 0 :(得分:1)
经过无数次的尝试,我做了一些有用的事情。但是,我仍然非常困惑,如果有人可以解释它会真的有帮助。如果post
为user.save()
,则我为其工作所做的唯一更改是true
明确保存。以下是修改后的addPost()
(唯一更改的行标记为// save the post explicitly!!
,其他地方没有其他更改。
def addPost() {
def user = User.findByLoginId(params.id)
if (user) {
def post = new Post(params)
user.addToPosts(post)
if (user.save()) {
post.save(flush: true, failOnError: true) // save the post explicitly!!
flash.message = "Successfully created Post"
} else {
flash.message = "Invalid or empty post"
}
} else {
flash.message = "Invalid User Id"
}
redirect(action: 'timeline', id: params.id)
}
一个注意事项:即使我if (user.save(flush: true))
而不仅仅if (user.save())
,但未将帖子的显式保存包含为post.save(flush: true, failOnError: true)
,它也无效。我已明确保存帖子。
由于保存user
应该自动保存post
,这种行为仍然让我感到困惑。如果有人能解释这种行为,那将非常有帮助。感谢那些花时间研究这个问题的人。
更新 -
下面的Peter Ledbrook的解释(link here):
您使用的是哪个版本的Grails?我刚刚尝试使用Grails 2.2.1 并在该测试中得到NullPointerException。升级到2.2.4固定 那个特别的问题。可能是Grails问题。
仅供参考,您甚至不需要明确保存用户实例 坚持邮政的行动。所以有些事情肯定是错的。该 级联保存被破坏,可能在Grails的模拟数据库中 在单元测试期间使用的实现。我猜是的 应用程序在正常运行时工作正常(例如通过run-app)。
答案 1 :(得分:0)
我有同样的错误,我认为问题出在
def post = new Post(params)
正在添加
params.id: "chuck_norris"
到Post创建,但奇怪的是没有params.content。
我通过将线路改为
解决了我的问题def post = new Post(content: params.content)