我试图在控制器上进行相当简单的集成测试,虽然它在应用程序中都有效,但集成测试会因NPE而失败。我还是Grails 2.5.0 / Spock测试的新手,我确信我必须在这里做一些愚蠢的事情,但无法弄明白。我认为在集成测试中(与单元测试不同),行为应该与应用程序运行时的行为非常相似。
想象一下以下(非常简化)的场景来重现我遇到的问题。一个简单的3级域对象层次结构:
class Author {
String name
static hasMany = [books: Book]
}
class Book {
String name
static belongsTo = [author: Author]
static hasMany = [chapters: Chapter]
}
class Chapter {
String name
static belongsTo = [book: Book]
}
一个控制器,只需一个动作即可接收单个作者拥有的所有书籍中的所有章节(即访问2个级别的集合):
class AuthorController {
def listChapters(Long id) {
Author a = Author.get(id)
respond a.books.chapters.flatten()
}
}
我在bootstrap中生成了示例数据,上面的控制器方法可以工作,并返回单个作者在平面的1级JSON数组中所有书籍的所有章节(如预期的那样)。但集成测试与NPE崩溃,好像从未初始化了延迟加载的集合,即Cannot get property 'chapters' on null object
以下是测试:
class AuthorIntegrationSpec extends IntegrationSpec {
def controller
def setup() {
controller = new AuthorController()
}
def cleanup() {
controller = null
}
def "chapters of a single author can be retrieved"() {
when: "I call the listChapters action"
controller.response.format = 'json'
controller.request.method = 'GET'
controller.params.id = 1
controller.listChapters()
then: "I get 200 OK"
controller.response.status == 200
}
}
我确定我必须遗漏一些非常明显的东西,但已经花费了3个小时就无济于事。我会很感激为什么会崩溃,或者我的方式与此集成测试完全错误?
更新:此处带有样本数据的BootStrap代码:
def init = { servletContext ->
def rado = new Author(name: 'Rado').save(flush:true)
def joe = new Author(name: 'Joe').save(flush:true)
def book1 = new Book(name: 'Book1', author: rado).save(flush:true)
def book2 = new Book(name: 'Book2', author: rado).save(flush:true)
def book3 = new Book(name: 'Book3', author: rado).save(flush:true)
def book4 = new Book(name: 'Book4', author: joe).save(flush:true)
new Chapter(name: 'Chapter 1', book: book1).save(flush:true)
new Chapter(name: 'Chapter 2', book: book1).save(flush:true)
new Chapter(name: 'Chapter 3', book: book1).save(flush:true)
new Chapter(name: 'Chapter 4', book: book1).save(flush:true)
new Chapter(name: 'BChapter 1', book: book2).save(flush:true)
new Chapter(name: 'BChapter 2', book: book2).save(flush:true)
}