在 grails 中对 RestfulController 进行单元测试时默认索引操作的奇怪行为
Odd behavior with default index action when unit testing a RestfulController in grails
我使用 grails 2.4.4(和 2.4.5)创建了以下微型应用程序。
grails create-app example
cd example
grails create-domain-class Book
然后编辑 Book.groovy 看起来像
package example
class Book {
String title;
static constraints = {
}
}
然后添加了一个基本的控制器
grails> create-controller example.book
| Created file grails-app/controllers/example/BookController.groovy
| Created file grails-app/views/book
| Created file test/unit/example/BookControllerSpec.groovy
并修改控制器以扩展 RestfulController。
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
}
连接 UrlMappings 以在 /api/books.
上将书籍作为资源提供
class UrlMappings {
static mappings = {
"/api/books"(resources: "book")
"/"(view:"/index")
"500"(view:'/error')
}
}
最后但并非最不重要的一点是,在 BootStrap.groovy 中制作了几本书。
import example.*
class BootStrap {
def init = { servletContext ->
new Book(title: "Book 1").save(failOnError:true);
new Book(title: "Book 2").save(failOnError:true);
new Book(title: "Book 3").save(failOnError:true);
}
def destroy = {
}
}
然后 grails run-app
一切看起来都很好。 example.Book 控制器出现在索引页中。三本书可以看成json或xml。
那么现在进行单元测试。
将 BookControllerSpec 编辑成如下所示
package example
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification
@TestFor(BookController)
@Mock(Book)
class BookControllerSpec extends Specification {
def setup() {
new Book(title: "Unit Test Book 1").save(failOnError:true);
new Book(title: "Unit Test Book 2").save(failOnError:true);
new Book(title: "Unit Test Book 3").save(failOnError:true);
}
void "My Setup Worked"() {
given: "My setup ran"
when: "I ask for the count"
then: "I should get back 3"
Book.count() == 3;
}
void "I can access my Controller index method"() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
}
第一次测试通过,第二次失败。失败测试中 println 的输出是一个空的 xml 图书列表。
<?xml version="1.0" encoding="UTF-8"?><list />
在调查发生了什么时,我查看了父级 class (RestfulController) 的索引方法。
将该方法复制到 BookController 中,单元测试将开始通过。
-- 带有复制索引方法的新版本 BookController --
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond listAllResources(params), model: [("${resourceName}Count".toString()): countResources()]
}
}
那么,为了让 RestfulController 的 index 方法工作而无需将 index 方法复制到 BookController 中,我还需要向规范中添加什么吗?
知道为什么在直接从 BookController 执行时调用 listAllResources
有效,但从 RestfulController 执行时 returns 没有行。
上面的单元测试是为Grails 2.3编写的Grails In Action一书中描述的其余单元测试的修改版本。 http://www.manning.com/gsmith2/
使用 Grails 进行单元测试可能会非常棘手。我从来没有确定究竟是什么导致了这次失败,但我确实发现了另一个奇怪的地方。
将失败的测试插入规范文件两次将导致第一次失败,但第二次通过。
void "first call to index doesn't work."() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
void "2nd call to index works"() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
grails 测试框架内部的某些东西在第一次调用时没有成功连接该索引方法。
我没有进一步挖掘,而是将其重写为集成测试,它开始正常运行。
通过 Grails 单元测试,您可以免费获得很多魔法,但是当魔法不起作用时,最好尝试不同的测试阶段。
我使用 grails 2.4.4(和 2.4.5)创建了以下微型应用程序。
grails create-app example
cd example
grails create-domain-class Book
然后编辑 Book.groovy 看起来像
package example
class Book {
String title;
static constraints = {
}
}
然后添加了一个基本的控制器
grails> create-controller example.book
| Created file grails-app/controllers/example/BookController.groovy
| Created file grails-app/views/book
| Created file test/unit/example/BookControllerSpec.groovy
并修改控制器以扩展 RestfulController。
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
}
连接 UrlMappings 以在 /api/books.
上将书籍作为资源提供class UrlMappings {
static mappings = {
"/api/books"(resources: "book")
"/"(view:"/index")
"500"(view:'/error')
}
}
最后但并非最不重要的一点是,在 BootStrap.groovy 中制作了几本书。
import example.*
class BootStrap {
def init = { servletContext ->
new Book(title: "Book 1").save(failOnError:true);
new Book(title: "Book 2").save(failOnError:true);
new Book(title: "Book 3").save(failOnError:true);
}
def destroy = {
}
}
然后 grails run-app
一切看起来都很好。 example.Book 控制器出现在索引页中。三本书可以看成json或xml。
那么现在进行单元测试。
将 BookControllerSpec 编辑成如下所示
package example
import grails.test.mixin.TestFor
import grails.test.mixin.Mock
import spock.lang.Specification
@TestFor(BookController)
@Mock(Book)
class BookControllerSpec extends Specification {
def setup() {
new Book(title: "Unit Test Book 1").save(failOnError:true);
new Book(title: "Unit Test Book 2").save(failOnError:true);
new Book(title: "Unit Test Book 3").save(failOnError:true);
}
void "My Setup Worked"() {
given: "My setup ran"
when: "I ask for the count"
then: "I should get back 3"
Book.count() == 3;
}
void "I can access my Controller index method"() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
}
第一次测试通过,第二次失败。失败测试中 println 的输出是一个空的 xml 图书列表。
<?xml version="1.0" encoding="UTF-8"?><list />
在调查发生了什么时,我查看了父级 class (RestfulController) 的索引方法。
将该方法复制到 BookController 中,单元测试将开始通过。
-- 带有复制索引方法的新版本 BookController --
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond listAllResources(params), model: [("${resourceName}Count".toString()): countResources()]
}
}
那么,为了让 RestfulController 的 index 方法工作而无需将 index 方法复制到 BookController 中,我还需要向规范中添加什么吗?
知道为什么在直接从 BookController 执行时调用 listAllResources
有效,但从 RestfulController 执行时 returns 没有行。
上面的单元测试是为Grails 2.3编写的Grails In Action一书中描述的其余单元测试的修改版本。 http://www.manning.com/gsmith2/
使用 Grails 进行单元测试可能会非常棘手。我从来没有确定究竟是什么导致了这次失败,但我确实发现了另一个奇怪的地方。
将失败的测试插入规范文件两次将导致第一次失败,但第二次通过。
void "first call to index doesn't work."() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
void "2nd call to index works"() {
given: "My setup ran"
when: "I call index"
request.method="GET"
response.format="xml"
controller.index();
then: "I get an XML object back with 3 books"
println response.contentAsString
response.xml.book*.title.size()==3
}
grails 测试框架内部的某些东西在第一次调用时没有成功连接该索引方法。
我没有进一步挖掘,而是将其重写为集成测试,它开始正常运行。
通过 Grails 单元测试,您可以免费获得很多魔法,但是当魔法不起作用时,最好尝试不同的测试阶段。