2015-05-05 19 views
0

使用grails 2.4.4(和2.4.5)我创建了下面的小应用程序。单元测试grails中的RestfulController时使用默认索引操作的奇怪行为

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 />

在调查发生了什么事情我看着父类(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的索引方法,而无需工作将索引方法复制到BookController中?

任何想法为什么直接从BookController执行时调用listAllResources的原因,但从RestfulController执行时不返回任何行。

上面的单元测试是为grails 2.3编写的Grails In Action书中描述的其余单元测试的修改版本。 http://www.manning.com/gsmith2/

回答

0

单元测试可以变得非常棘手与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单元测试可以免费获得很多魔法,但是当魔法失效时,尝试不同的测试阶段可能会更好。

相关问题