在控制器测试中创建的域对象不会在数据库中结束

时间:2013-12-19 17:35:38

标签: grails controller mocking

我一直在尝试遵循Grails in Action(GiA MEAP,第2版),但我偶尔偶然发现一些即使花了很多时间也无法弄清楚的问题。这是其中之一。抱歉,这个问题与GiA中的示例相关(v13,ch6,清单6.x中的PostControllerSpec,第144页),但我将尝试使用精简但完整的代码来捕获问题。我有两个域模型,UserPost

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

我的问题是为什么上述测试失败了,即使它成功创建了帖子?我花了很多时间试图找出错误,但还没有运气。

2 个答案:

答案 0 :(得分:1)

经过无数次的尝试,我做了一些有用的事情。但是,我仍然非常困惑,如果有人可以解释它会真的有帮助。如果postuser.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)