Using grails 2.4.4 (and 2.4.5) I created the following tiny application.
grails create-app example
cd example
grails create-domain-class Book
Then edited Book.groovy to look like
package example
class Book {
String title;
static constraints = {
}
}
Then added a basic Controller
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
and modify the controller to extend RestfulController.
package example
import grails.rest.*
class BookController extends RestfulController<Book> {
static responseFormats=['json','xml']
BookController() {
super(Book)
}
}
Hook up the UrlMappings to serve up the books as a resource at /api/books.
class UrlMappings {
static mappings = {
"/api/books"(resources: "book")
"/"(view:"/index")
"500"(view:'/error')
}
}
and last but not least, built a few books in 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 = {
}
}
then grails run-app
And everything looks good. The example.Book controller shows up in the index page. The three books can be viewed as json or xml.
So now to the unit test.
Edit the BookControllerSpec to look like this
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
}
}
The first test passes, and the 2nd one fails. The output from the println in the failing test is an empty xml list of books.
<?xml version="1.0" encoding="UTF-8"?><list />
While investigating what was going on I looked at the index method of the parent class (RestfulController).
By copying that method into BookController, the unit test will start passing.
-- new version of BookController with copied index method --
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()]
}
}
So is there anything else I need to add to the Spec in order to get the index method of the RestfulController to work without having to copy the index method into the BookController?
Any idea why the call to listAllResources
works when executed directly from BookController, but returns no rows when executed from the RestfulController.
The unit test above is a modified version of the rest unit tests described in the Grails In Action book, which is written for grails 2.3. http://www.manning.com/gsmith2/