Showing posts with label testing. Show all posts
Showing posts with label testing. Show all posts

Friday, 17 September 2010

Clean TagLib Tests

Despite my best efforts I've always found it hard to write clean taglib tests. Now thanks to Spock and GroovyShell things are getting easier...

void setup() {
mockDomain Invoice
}

def "Attachments icon has correct markup"() {
given:
Invoice invoice = new InvoiceBuilder().buildAndSave()

when:
renderAttachmentsIcon([target: invoice])

then:
valueOf('img.@id') == "toggle-attachments-${invoice.id}"
valueOf('script') == "\$('#toggle-attachments-${invoice.id}').bind('click', Books.attachments.toggle);"
}

Because I want to use GPath make assertions about the resulting HTML I've overriden TagLibSpec's methodMissing closure as follows...

def methodMissing(String name, args) {
String html = super.methodMissing(name, args)
createDocument(html)
}

void createDocument(String html) {
String xml = "<results>${html}</results>"
document = new XmlSlurper().parseText(xml)
}

And added helper methods for evaluating GPath expressions...

String valueOf(String gPath) {
evaluate(gPath).text()
}

GPathResult evaluate(String gPath) {
new GroovyShell(new Binding(document: document)).evaluate("document.${gPath}")
}

There's a little bit more to this story unfortunately...

Firstly my taglibs use the MarkupBuilder and when run from my Spock test this only outputs the opening tag of the first element I genererate. I haven't had a chance to look into this yet, but a workaround is to add "out << '' to the end of the taglib method

Secondly Spock interactions aren't yet as powerful as gmock, so I usually end up adding code to (g)mock grails taglibs.

Thirdly another one of my tests outputs &nbsp; in the HTML which causes the XML parsing to barf. The solution is to map the &nbsp entity to a known character (in this case underscore).

Finally there was a bug in Grails 1.3.3 / Spock 0.5 which breaks mockDomain. This is reportedly fixed in 1.3.4 and the latest Spock code, but I haven't tried upgrading yet.

Here's how things really look...

@WithGMock
class MetaAttachmentsTagLibSpec extends TagLibSpec {

def g
def document

void setup() {
g = mock()
mock(tagLib).getG().returns(g).stub()
mockDomain Invoice

PluginManagerHolder.pluginManager = [hasGrailsPlugin: { String name -> true }] as GrailsPluginManager // Workaround for JIRA GRAILS-6482
}

def cleanup() {
PluginManagerHolder.pluginManager = null // Workaround for JIRA GRAILS-6482
}

def "Attachments icon has correct markup"() {
given:
Invoice invoice = new InvoiceBuilder().buildAndSave()
g.resource(instanceOf(Map)).returns '/foo.jpg'

when:
renderAttachmentsIcon([target: invoice])

then:
valueOf('img.@id') == "toggle-attachments-${invoice.id}"
valueOf('img.@src') == "/foo.jpg"
valueOf('script') == "\$('#toggle-attachments-${invoice.id}').bind('click', Books.attachments.toggle);"
}

def methodMissing(String name, args) {
String html
play {
html = super.methodMissing(name, args)
}
createDocument(html)
}

def createDocument(String html) {
String xml = """<!DOCTYPE html [<!ENTITY nbsp "_">]>\n<results>${html}</results>"""
document = new XmlSlurper().parseText(xml)
}
}

And the method under test...

def renderAttachmentsIcon = { Map attrs, def body ->
String targetId = attrs.target.id
String iconId = "toggle-attachments-${targetId}"
String imgSrc = g.resource(dir:'/images/skin', file:'paperclip.png')

MarkupBuilder builder = new MarkupBuilder(out)
builder.img(id: iconId, src: imgSrc)
builder.script(type:'text/javascript') {
mkp.yield "\$('#${iconId}').bind('click', Books.attachments.toggle);"
}
out << '' // flush for unit tests
}

Sunday, 1 February 2009

Testing Url Mappings

If you have to support legacy urls, want some quick feedback when putting your url mappings together or want to verify the parameters are being parsed correctly, here's a little grails integration-test snippet that you may find useful:

def grailsUrlMappingsHolderBean

void testMappingForPersonController() {
def mappingInfo = grailsUrlMappingsHolderBean.match('/person/pain')
assertEquals 'person', mappingInfo.controllerName
assertEquals 'show', mappingInfo.actionName
assertEquals 'pain', mappingInfo.parameters.id
}

The grails testing plugin provides custom assertions for doing this and more (assertUrlMapping etc.).

kthxbye

Thursday, 22 January 2009

On selenium testing...

I updated my blog to list some useful tips for writing more resilient Selenium XPath tests. 

Please feel free to add more suggestions or otherwise comment.

Saturday, 18 October 2008

Gmock - Groovy Mock

Mock Object are key players in Unit Testing. Groovy support natively some mock object with MockFor and StubFor but their functionality are quite limited and the syntax heavy - you'll understand when you nest 7 'use' closures.

Gmock aims to simplified mocking in Groovy through a intuitive syntax and a great readability. In a nutshell a Gmock test look like:

void testTree(){
def mockTree = mock()
mockTree.load('fruit').returns('apple')
play {
assertEquals "apple", mockTree.load('fruit')
}
}
This is it!. Expectation are being setup by calling normal method on you mock object. The code under test is executed within the play closure and your mocks are automatically verified after it.

The current version gmock-0.2 support the most basic functionality you would expect from a mocking framework. Version 0.3 should see static method mocking and property mocking. Future development are described in the Roadmap.

Thursday, 25 September 2008

Effective testing on Grails

Gus and I will be presenting on "Effective Testing on Grails" at the London Groovy and Grails User Group next week Wed 1st of October, @18.30. Please feel free to attend if this seems relevant to you. A synopsis of the talk and registration for the event are all available at the following link.

http://skillsmatter.com/event/java-jee/ggug

Thursday, 28 August 2008

Unit Testing Controllers With The Testing Plugin

Grails JUnit tests are, as you are no doubt aware, divided into unit tests and integration tests. Unit tests run fast, aren't dependent on the database or Spring context but mean you have to live without all the dynamic magic that Grails attaches to your artefacts (controllers, taglibs, domain classes, etc.) This can certainly lead to some sub-optimal test code.

Lets consider an example. I want to write some test coverage for a controller that looks up a domain object based on a parameter and places it in the model. Usual disclaimer: of course, in the real world I'd have written the test first, etc. Nothing much to the controller:
class LolrusController {
def show = {
def myLolrus = Lolrus.findByName(params.lolrusName)
render(view: 'lolrus', model: [lolrus: myLolrus])
}
}
and here's the domain class:
class Lolrus {
String name
String mood
static hasMany = [posessions: Item]
static constraints = {
name(unique: true)
posessions(validator: { posessions ->
return posessions.any { it.type == 'bukkit' }
})
}
String toString() { "Lolrus[$name]" }
boolean equals(Object o) { return o instanceof Lolrus && o.name == name }
int hashCode() { return 37 * name.hashCode() }
}
Okay, so a reasonable test (ignoring edge cases for now) could be to create a couple of domain objects, make a request to the controller and verify that the correct domain object is retrieved. Simple, right?:
class LolrusControllerTests extends GroovyTestCase {
def controller
def lolrus1, lolrus2

void setUp() {
controller = new LolrusController()

lolrus1 = new Lolrus(name: 'Hugh')
lolrus2 = new Lolrus(name: 'Alan')
[lolrus1, lolrus2]*.save(flush: true)
}

void testShowActionFindsCorrectLolrusAndSticksItInModel() {
controller.params.lolrusName = lolrus1.name
def model = controller.show()
assertEquals('/lolrus/show', controller.modelAndView.viewName)
assertEquals(lolrus1, controller.modelAndView.model.lolrus)
}
}
Unfortunately, not so simple. The test fails and reports
expected:<Lolrus[Hugh]> but was:<null>
What we've forgotten of course is that Lolrus has a mandatory field mood and a custom validation on its posessions association that requires it to have a bukkit. The two Lolrus instances we tried to create in setUp couldn't be saved. Great. Well, let's just add that to the test set up:
    void setUp() {
controller = new LolrusController()

lolrus1 = new Lolrus(name: 'Hugh', mood: 'worryingly cheerful')
lolrus1.addToPosessions new Item(type: 'bukkit')

lolrus2 = new Lolrus(name: 'Alan', mood: 'mildly distressed')
lolrus2.addToPosessions new Item(type: 'bukkit')

[lolrus1, lolrus2].each {
assert it.save(flush: true), it.errors
}
}
Okay, it works but look how much of what's going on in setUp is actually nothing to do with anything the test cares about. This is a very simple example with a single test case. Imagine how much worse that setUp method is going to get when we add more tests or the Lolrus domain object gets more complex... Imagine the refactoring required when the Lolrus domain object acquires other constraints... Imagine the query is more complex requiring more domain objects to be set up to really prove it works...

Okay, we could mock the Lolrus class, but the code for that isn't much cleaner and alarm bells start ringing when you start using mocks in an integration test. Shouldn't this be a unit test? Sure, that would be nice but we're using the render dynamic method in our controller, we'll have to mock that out on the metaClass, mock out the findByName method on Lolrus, etc. etc. Too much hassle right? Not any more.
grails install-plugin testing
Allows us to (among other things) write controller unit test cases like this:
class LolrusControllerUnitTests extends grails.test.ControllerUnitTestCase {
def controller
def lolrus1, lolrus2

void setUp() {
super.setUp()
controller = new LolrusController()

lolrus1 = new Lolrus(name: 'Hugh')
lolrus2 = new Lolrus(name: 'Alan')
mockDomain(Lolrus, [lolrus1, lolrus2])
}

void testShowActionFindsCorrectLolrusAndSticksItInModel() {
controller.params.lolrusName = lolrus1.name
controller.show()
assertEquals('show', renderArgs.view)
assertEquals(lolrus1, renderArgs.model.lolrus)
}
}
Much neater. We're not worrying about the constraints required to set up valid Lolrus instances, but neither are we having to mock out the findByName method.

The mockDomain method allows you to set up some domain objects for which all the usual Grails domain class dynamic methods will work - including findBy..., addTo..., etc. The collection passed as the second argument to mockDomain defines all the instances that exist and they can be retrieved just as a real domain object could. The mockDomain method co-exists happily with regular Groovy mocks and the testing plugin also provides some lighter mocking capabilities via its own mockFor method.

Additionally the controller's render method and params property work without any work on our part. This is true of all the dynamic properties and methods of the controller, request, response, params, controllerName, redirect, etc.

Not only that, but this test runs faster than the integration test as it's not configuring the Spring container or an in-memory database. That speed difference only grows as the project becomes more complex - it's a couple of seconds between the two tests here, but in a more complex project it's a lot more.

There are a couple of gotchas (the plugin isn't fully mature). First off you need to remember to call super.setUp() in order that all the Grails stuff happens. The ControllerUnitTestCase class will figure out based on the class naming convention which controller class it needs to mock up. Secondly, the domain class get method doesn't seem to work with non-standard identifier types at the moment (i.e. anything other than a Long). However, this unit testing support is intended to get rolled into Grails 1.1 so it should improve rapidly.

This is only part of what the testing plugin can do. Support for unit testing domain class constraints, etc. is also included. I've also been working on a TagLibUnitTestCase that works along the same lines as ControllerUnitTestCase.

Tuesday, 12 August 2008

Overriding constraints

I didn't know you could override constraints until the other day. Here is an example of overriding and then testing the constraints with the new testing plugin. Testing constraints with the new plugin is so easy I suggest every team uses it. We started using it for our team. Here's the code:

class Parent {
String name
String body

static constraints = {
name(blank: false, nullable:false)
body(blank: false, nullable:false)
}
}

class Child extends Parent{
static constraints = {
name(blank: true)
body(blank: true)
}
}


------------Tests using the testing plugin ---------------
import grails.test.GrailsUnitTestCase
import grails.test.MockUtils

class ChildTests extends GrailsUnitTestCase{

void testConstraints() {
// Mock the validate() method.
registerMetaClass(Child)
MockUtils.prepareForConstraintsTests(Child)

registerMetaClass(Parent)
MockUtils.prepareForConstraintsTests(Parent)

def testInstance = new Child()
def errors = testInstance.validate()
assertEquals 0, errors.size()

// Test the parent constraints
testInstance = new Parent(name:' ' )
errors = testInstance.validate()
assertEquals 2, errors.size()
assertEquals "blank", errors["name"]
assertEquals "nullable", errors["body"]
}
}

Happy testing :)

Wednesday, 2 July 2008

Good Unit Testing Practice

A common but misguided practice that is often seen in unit test designs, is to test some kind of a false moniker on an object, rather than the actual properties of the object that should be tested.
For example. We have a simple "Dog" object and we want to get a list of all Dogs of a certain breed,ordered by the date of birth of the dog.



class Dog{
Breed breed;
Date dob;
String name;

}


When testing using this moniker style we might test like this (notice that i have introduced errors but the tests still pass since they are testing the moniker and not the properties that should be tested)...


void testGetAllAlsationsOrderedByAgeUsingMonikers(){

Dog fido = new Dog(name:'alsation born last year', dob: '01/01/2006', breed:Breed.ALSATION)
Dog woofer = new Dog(name:'alsation born 2 years ago', dob:'01/01/2008',breed:Breed.LABRADOR)
Dog mutley = new Dog(name:'alsation born this year', dob: '01/01/2008', breed:Breed.ALSATION)

List dogs = DogService.getDogByBreed(Breed.ALSATION)
assertTrue 'alsation born 2 years ago', dogs[0].name
assertTrue 'alsation born born last year', dogs[1].name
assertTrue 'alsation born this year', dogs[2].name

}


the problem with this is that we are actually not testing for the real properties that we should be testing for, and we are relying on the fact that we didn't introduce any errors in our test set up. which, incidentally,we did, just to prove the point!


A much better way to test this would be the following...



void testGetAllAlsationsOrderedByAgeCorrectly(){

Dog fido = new Dog(name:'not relevant', dob: '01/01/2006', breed:Breed.ALSATION)
Dog woofer = new Dog(name:'not relevant', dob: '01/01/2008', breed:Breed.LABRADOR)
Dog mutley = new Dog(name:'not relevant', dob: '01/01/2008', breed:Breed.ALSATION)

List dogs = DogService.getDogByBreed(Breed.ALSATION)
assertEquals(2, dogs.size())
assertTrue( isOrderedByDateOfBirth( dogs) )
assertTrue ( dogs.every{ Dog dog -> dog.breed=Breed.ALSATION} )

}

private boolean isOrderedByDateOfBirth( List dogs) {

def dobComparator = [compare:{Dog dog1, Dog dog2 ->
dog1.dob == dob2.dob ? 0 :dog1.dob > dog2.dob? 1 : -1 }] as Comparator
return dogs.sort(dobComparator) == dogs


}

Thursday, 15 May 2008

Fear & Loathing in the MetaClassRegistry

On your testing travels you may, on occasion, mock methods out on certain classes using the old metaClass trick:
MyLovelyClass.metaClass.'static'.myLovelyMethod = { //crazy stuff here }

Now the fun with this approach is that when/how/where/how long/huh? feeling you get when you realize that other tests in the the suite start failing - dreaded test bleed (insert dramatic soundclip here).

Thanks to the Groovy method reference operation (&) and the MetaClassRegistry all can be made well, without having to resort to sticking all the initial method references in a map (or similar) and trying to stuff it all back on the original class in your tearDown():
void tearDown() {
def remove = GroovySystem.metaClassRegistry.&removeMetaClass
remove MyLovelyClass
}

Hmmm. I feel a bit lightheaded. Maybe you should drive.