Source:
The core of the testing plugin is the grails.test.GrailsUnitTestCase class.
mockDomain(class, testInstances)
Takes a class and makes mock implementations of all the domain class methods.
example:
void testSomething() {
def testInstances=[]
mockDomain(Song, testInstances)
assertEquals(0, Song.count())
new Song(name:"Supper's Ready").save()
assertEquals(1, Song.count())
}
example:
def testInstances = [ new Item(code: "NH-4273997", name: "Laptop"),
new Item(code: "EC-4395734", name: "Lamp"),
new Item(code: "TF-4927324", name: "Laptop") ]
mockDomain(Item, testInstances)
mockForConstraintsTests(class, testInstances)
Highly specialised mocking for domain classes and command objects that allows you to check whether the constraints are behaving as you expect them to.
example:
class Book {
String title
String author
static constraints = {
title(blank: false, unique: true)
author(blank: false, minSize: 5)
}
}
class BookTests extends GrailsUnitTestCase {
void testConstraints() {
def existingBook = new Book(title: "Misery", author: "Stephen King")
mockForConstraintsTests(Book, [ existingBook ])
// Validation should fail if both properties are null.
def book = new Book()
assertFalse book.validate()
assertEquals "nullable", book.errors["title"]
assertEquals "nullable", book.errors["author"]
// So let's demonstrate the unique and minSize constraints.
book = new Book(title: "Misery", author: "JK")
assertFalse book.validate()
assertEquals "unique", book.errors["title"]
assertEquals "minSize", book.errors["author"]
// Validation should pass!
book = new Book(title: "The Shining", author: "Stephen King")
assertTrue book.validate()
}
}
mockFor(class)
This range determines how many times you expect the method to be called, so if the number of invocations falls outside of that range then an assertion error will be thrown. If no range is specified, a default of "1..1" is assumed.
The closure arguments should match the number and types of the mocked method, but otherwise you are free to add whatever you want in the body.
example:
def strictControl = mockFor(MyService)
strictControl.demand.someMethod(0..2) { String arg1, int arg2 -> … }
strictControl.demand.static.aStaticMethod {-> … }
example:
class MyService { def otherService
String createSomething() {
def stringId = otherService.newIdentifier()
def item = new Item(code: stringId, name: "Bangle")
item.save()
return stringId
}
int countItems(String name) {
def items = Item.findAllByName(name)
return items.size()
}
}
import grails.test.GrailsUnitTestCase
class MyServiceTests extends GrailsUnitTestCase {
void testCreateSomething() {
// Mock the domain class.
mockDomain(Item)
// Mock the "other" service.
String testId = "NH-12347686"
def otherControl = mockFor(OtherService)
otherControl.demand.newIdentifier(1..1) {-> return testId }
// Initialise the service and test the target method.
def testService = new MyService()
testService.otherService = otherControl.createMock()
def retval = testService.createSomething()
// Check that the method returns the identifier returned by the
// mock "other" service and also that a new Item instance has
// been saved.
def testInstances = Item.list()
assertEquals testId, retval
assertEquals 1, testInstances.size()
assertTrue testInstances[0] instanceof Item
}
void testCountItems() {
// Mock the domain class, this time providing a list of test
// Item instances that can be searched.
def testInstances = [ new Item(code: "NH-4273997", name: "Laptop"),
new Item(code: "EC-4395734", name: "Lamp"),
new Item(code: "TF-4927324", name: "Laptop") ]
mockDomain(Item, testInstances)
// Initialise the service and test the target method.
def testService = new MyService()
assertEquals 2, testService.countItems("Laptop")
assertEquals 1, testService.countItems("Lamp")
assertEquals 0, testService.countItems("Chair")
}
}
mockLogging(class, enableDebug = false)
Adds a mock "log" property to a class. Any messages passed to the mock logger are echoed to the console.
mockController(class)
Adds mock versions of the dynamic controller properties and methods to the given class. This is typically used in conjunction with the ControllerUnitTestCase class.
Mock controller properties available in unit tests
- request
- response
- session
- flash
- params
- redirectArgs
- renderArgs
- template
- modelAndView
example:
class PostControllerUnitTests extends grails.test.ControllerUnitTestCase {
void testShow() {
mockDomain(User, [
new User(userId: "glen"),
new User(userId: "peter") ]
this.controller.params.id = "peter"
def model = this.controller.show()
assertEquals "peter", model["viewUser"]?.userId
}
}
example - action returns a model:
void testList() {
mockDomain(Album, [ new Album(title: "Odelay"),
new Album(title: "Abbey Road"] )
def model = controller.list()
assertEquals 2, model.albumList.size()
}
example - Testing the Contents of the Response:
void testIndex() {
controller.index()
assertEquals "Welcome to the gTunes store!", controller.response.contentAsString
}
example - contents of render args
render(view: "show", model:[album:Album.get(params.id)])
void testShow() {
mockDomain(Album, new Album(id:1, title: "Aha Shake Heartbreak"))
mockParams.id = 1
controller.show()
assertEquals "show", renderArgs.view
assertEquals 1, renderArgs.model.album.id
assertEquals "Aha Shake Heartbreak", renderArgs.model.album.title
}
mockParams: this property provides a mock implementation of the params object that you can populate with values before calling the controller. In addition to a mock implementation of the params object, the ControllerUnitTestCase class provides the following properties that mock various aspects of the controller API:
- mockRequest
- mockResponse
- mockSession
- mockParams
- mockFlash
note:ControllerUnitTestCase not support some dynamic method. For instance: bindData(). Then is better use integration testing, or you can add this method to controller:
example:
this.controller.metaClass.bindData = { obj, params ->
params.each { key, value ->
obj."$key" = value
}
}
this.controller.metaClass.getGrailsApplication = {-> return [config: [:]] }
example - redirect:
//in controller
redirect(controller: "post", action: "list")
// in test
assertEquals "post", this.controller.redirectArgs["controller"]
assertEquals "list", this.controller.redirectArgs["action"]
example - testing action:
def register = {
if(request.method == 'POST') {
def u = new User(params)
if(u.password != params.confirm) {
u.errors.rejectValue("password", "user.password.dontmatch")
return [user:u]
}else if(u.save()) {
session.user = u
redirect(controller:"store")
}else {
return [user:u]
}
}
}
void testRegistrationFailed() {
mockRequest.method = 'POST'
mockDomain(User)
mockParams.login = ""
def model = controller.register()
assertNull mockSession.user
assert model
def user = model.user
assert user.hasErrors()
assertEquals "blank", user.errors.login
assertEquals "nullable", user.errors.password
assertEquals "nullable",
user.errors.firstName
assertEquals "nullable", user.errors.firstName
}
void testRegistrationSuccess() {
mockRequest.method = 'POST'
mockDomain(User)
mockParams.login = "joebloggs"
mockParams.password = "password"
mockParams.confirm = "password"
mockParams.firstName = "Joe"
mockParams.lastName = "Blogs"
def model = controller.register()
assertEquals 'store',redirectArgs.controller
assertNotNull mockSession.user
}
example - test Command
def login = { LoginCommand cmd ->
if(request.method == 'POST') {
if(!cmd.hasErrors()) {
session.user = cmd.getUser()
redirect(controller:'store')
}else {
render(view:'/store/index', model:[loginCmd:cmd])
}
}else {
render(view:'/store/index')
}
}
void testLoginUserNotFound() {
mockRequest.method = 'POST'
mockDomain(User)
MockUtils.prepareForConstraintsTests(LoginCommand)
def cmd = new LoginCommand(login:"fred", password:"letmein")
cmd.validate()
controller.login(cmd)
assertTrue cmd.hasErrors()
assertEquals "user.not.found", cmd.errors.login
assertEquals "/store/index", renderArgs.view
}
void testLoginPasswordInvalid() {
mockRequest.method = 'POST'
mockDomain(User, [new User(login:"fred", password:"realpassword")])
MockUtils.prepareForConstraintsTests(LoginCommand)
def cmd = new LoginCommand(login:"fred", password:"letmein")
cmd.validate()
controller.login(cmd)
assertTrue cmd.hasErrors()
assertEquals "user.password.invalid", cmd.errors.password
assertEquals "/store/index", renderArgs.view
}
void testLoginSuccess() {
mockRequest.method = 'POST'
mockDomain(User, [new User(login:"fred", password:"letmein")])
MockUtils.prepareForConstraintsTests(LoginCommand)
def cmd = new LoginCommand(login:"fred", password:"letmein")
cmd.validate()
controller.login(cmd)
assertFalse cmd.hasErrors()
assertNotNull mockSession.user
assertEquals "store", redirectArgs.controller
}
mockTagLib(class)
Adds mock versions of the dynamic taglib properties and methods to the given class. This is typically used in conjunction with the TagLibUnitTestCase class.
example:
class MyTagLib {
def repeat = { attrs, body ->
attrs.times?.toInteger().times { n ->
body(n)
}
}
}
<g:repeat times="3">Hello number ${it}</g:repeat>
import grails.test.*
class MyTagLibTests extends TagLibUnitTestCase {
MyTagLibTests() {
super(MyTagLib)
}
void testRepeat() {
tagLib.repeat(times: '2') {
'output<br/>'
}
assertEquals 'output<br/>output<br/>', tagLib.out.toString()
}
}
example:
One oddity is the empty map ([:]) as an argument, which is required because the tag
expects an attribute map as the first argument. If we were passing in at least one attri-
bute value, we could use the implicit syntax instead:
tagLib.isLoggedIn(attr1: "value") { ... }
In order to check whether the appropriate text has been generated by the tag, we
then perform a toString() on the out property and compare it to the expected value
mockConfig
In your controller you have to use org.codehaus.groovy.grails.commons.ConfigurationHolder. Then is very simple used mockConfig.
example:
private void takeOverDemoTestsets(school) {
def String[] ids = ConfigurationHolder.config.cz.svse.testy.demo.testsets.ids.split(",")
ids.each(){ id ->
def testset = Testset.get(id)
if(testset){
school.addToTestsets( testset.clone( school ) )
log.info(" add testset: ${testset.title}" )
}
}
}
class SchoolControllerTests extends grails.test.ControllerUnitTestCase {
protected void setUp() {
super.setUp()
mockDomain(Testset, [
new Testset(id:1, title:"one"),
new Testset(id:2, title:"two")
])
mockDomain(School,[])
mockConfig ('''
cz.svse.testy.demo.testsets.ids="1,2"
''')
}
protected void tearDown() {
super.tearDown()
}
void testTakeOverDemoTestsets() {
def school = new School()
assertEquals 0, school.testsets.size()
this.controller.takeOverDemoTestsets(school)
assertEquals 2, school.testsets.size()
}
}