Functional testing in Grails just got a bit sexier
Ok so here’s the 1.0 release in the spirit of “don’t worry be crappy” (can’t use that phrase enough these days) of a new functional testing plugin for Grails.
Actually its far from crappy already.
I have developed this partly as part of my work for my generous client Historic Futures Ltd. who agreed to open source this.
I plan to continue improving and maintaining it personally. It’s surely a bit rough around the edges but the benefits should be pretty obvious to those who use it.
So what are you waiting for? Run:
grails install-plugin functional-test
…but in a nutshell it is HTTP testing with HtmlUnit under the hood but the rest is pure groovy smarts, leveraging standard JUnit.
Example:
import functionaltestplugin.*
class TwitterTests extends FunctionalTestCase {
void testSearch() {
get('http://www.twitter.com')
click "Search"
assertStatus 200
assertContentContains "search"
form('searchForm') {
q = "#grails"
click "Search"
}
assertStatus 200
assertContentContains "#grails"
}
}
What’s special about this?
- Simplicity and “elasticity” by default = less fragile functional tests as a result
- Simple compact DSL/methods to learn – it tries to “do the right thing”. Let’s face it writing tests is really boring.
- Ability to get/post directly without browsing to a page first. eg REST testing
- No .properties configs
…and one more thing URL stacktrace. When a test fails the report includes a stack of the URLs at the point of failure. The xxx-out.txt file contains request parameters+headers, content received etc. This feature will be expanded in future to allow a proper request/response chain autopsy.
Caveats – things I definitely want to sort out pronto:
- currently test output is a bit ropey, I would like to do much nicer stuff with custom output xml and rendering
- no simple way to set post body
- no simple way to parse out json/xml responses yet
All this and more will come, with your support! Contribs also welcome!
ENJOY!





















24 Comments
Pratik Patel
January 8, 2009Marc, this looks pretty neat, looking forward to trying it out this weekend. How does this compare to canoo groovy-webtest?
j pimmel
January 8, 2009Looks very interesting! Would be interested to see more examples
I assume this uses latest HtmlUnit which can handle Javascript very well now yeah?
Scott Davis
January 8, 2009Strong sauce! I love reading about all of the testing goodness that is coming out with Grails 1.1. Keep up the good work – the syntax here is really nice.
Marc Palmer
January 8, 2009Scott – thanks. There’s more work to do but I feel this is a great start, with remarkably little code required to wrap up HtmlUnit thanks to all the groovy magic.
J Pimmel – more examples will come soon. Yes its the latest version of htmlunit at the time of development. Easy to update it too.
Pratik – Well for me this is just more Grails-y than webtest.
1. Your tests are in test/functional, your reports in test/reports
2. It re-uses JUnit and familiar paradigms eg assertContentContains (vs. Webtest’s verifyText)
3. The new plugin has strict and loose variants of assertions eg assertContentContains is case-INsensitive and ignores whitespace. This is what you want most of the time. assertContentContainsStrict does the check without whitespace/case stripping.
4. The DSL is simpler and thus needs less knowledge and documentation. You don’t need to know what -kind- of field you’re setting. In this plugin you do x = “y”, in webtest you do setInputField/setCheckbox/setRadiobutton.
5. You can trivially run the same tests against a different absolute url: grails functional-tests http://someprerelease-server
6. Pure AJAX and REST testing should be less verbose and it is trivial to do a POST to a url with some params in the body.
Peter Ledbrook
January 13, 2009And the major bonus over WebTest: it’s not handicapped by relying on Ant tasks! That is the biggest problem with WebTest and the reason I have never liked it much. Just try mixing your WebTest code with some normal Groovy – weird stuff happens because the execution order isn’t what you expect.
Great work Marc and I hope I can contribute in the future!
Robert
January 21, 2009Hi Marc,
I’m looking forward to test your plugin. I tried to install it against Grails 1.0.4. But I can’t download it. Is it just working against Grails 1.1?
Dennis Carroll
January 26, 2009Although I have not yet used it, for me, installation of http://plugins.grails.org/grails-functional-test/tags/RELEASE_1_2_1/grails-functional-test-1.2.1.zip under Grails 1.0.4 appeared normal.
mallu
February 18, 2009Hi Marc,
I am using functional test plugin, this is my code wht i wrote
class AuthControllerFunctionalTests extends functionaltestplugin.FunctionalTestCase {
void testLogin() {
get(‘/auth’, ‘login’) {
username “john”
password “admin”
}
form(“loginForm”) {
username = “john”
password = “admin”
click “submit”
}
assertContentContains “login”
}
}
it is showing error when i run test case, i am using jsecurity plugin
No signature of method: AuthControllerFunctionalTests.get() is applicable for argument types: (java.lang.String, java.lang.String, AuthControllerFunctionalTests$_testLogin_closure1) values: {“/auth”, “login”, AuthControllerFunctionalTests$_testLogin_closure1@1f21412}
groovy.lang.MissingMethodException: No signature of method: AuthControllerFunctionalTests.get() is applicable for argument types: (java.lang.String, java.lang.String, AuthControllerFunctionalTests$_testLogin_closure1) values: {“/auth”, “login”, AuthControllerFunctionalTests$_testLogin_closure1@1f21412}
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.codehaus.groovy.runtime.MetaClassHelper.doConstructorInvoke(MetaClassHelper.java:535)
at groovy.lang.MetaClassImpl.doConstructorInvoke(MetaClassImpl.java:2356)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1255)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1185)
at groovy.lang.ExpandoMetaClass.invokeConstructor(ExpandoMetaClass.java:524)
at org.codehaus.groovy.runtime.InvokerHelper.invokeConstructorOf(InvokerHelper.java:809)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeNewN(ScriptBytecodeAdapter.java:230)
at functionaltestplugin.FunctionalTestCase.invokeMethod(FunctionalTestCase.groovy:205)
at
please help to get out of problem.
thanks
mallu.
Marc Palmer
February 19, 2009Mallu – we have dealt with this on the grails user mailing list (the correct place for discussion really)… but to recap:
You are calling get() wrong. It takes a single string URI and a closure. Not two strings.
Also you appear to be duplicating your user + password assignments in the query and the form.
Felipe
March 6, 2009How about the WebRecorder for Canoo? Is there anything similar to it?
Thanks,
Felipe
Todd
April 5, 2009This looks like a really great start but the documentation is really half baked.
For example how do I do a regular expression match?
how do i get access to the result “out” object to look for something?
How do I do a file upload?
How do I do a assertContains on the resulting Javascript file?
Where is the API documentation?
Where is the link to the src?
Overall This post and the plugin page on grails.org look like a great quick start, but then this project is so new that my junior developer is only able to do very basic testing.
Appreciate the effort but this is far from “baked” or established as an opensource project.
Marc Palmer
April 14, 2009Todd – yes this is a new plugin, but its certainly not half baked.
The docs could do with some improvement but they’re not too bad at the moment. Sometimes you may need to know a little HtmlUnit which is used underneath it all for more advanced work.
The documentation is here http://grails.org/Grails+Functional+Testing
The src is in standard plugin SVN repo https://svn.codehaus.org/grails-plugins/grails-functional-test/trunk
To upload files, you access the file input field and set HTMLUnit properties as per the docs at http://htmlunit.sourceforge.net/apidocs/com/gargoylesoftware/htmlunit/html/HtmlFileInput.html
eg:
form(‘something’) {
yourFileInputFieldName.contentType = “text/plain”
yourFileInputFieldName.data = new File(‘something.txt’).bytes
click “submit” // or whatever
}
Please raise jiras for any features you need.
Marc Palmer
April 14, 2009Felipe – no such recorder exists. It should be possible to adapt one like the Webtest one.
Its not a high priority at the moment – the tests are so easy to write and expressive, that there’s not that much to be gained, given that your tests will need to be maintained/edited manually after recording them anyway.
John
April 14, 2009Marc,
I have been using your plugin for about 2 weeks now and I have run into an issue as seen below…
form(‘someForm’) {
image.file = “photo.jpg” // image.file is a multipart file
click “submit”
}
when compiling, I receive an error declaring that .file (of image.file) is a missing method. Have you any thoughts on the matter?
I do highly appreciate you (and people like you) taking the time to make a better mousetrap to improve product development for people like me.
Thanks in advance,
John
Gaurav
April 18, 2009Hi Marc,
I am using the impressive Functional Testing Plugin with my Grails App. I have this scenario and i am unable to find a solution for it. I have 2 buttons in my Form “APPROVE” and “DISAPPROVE” when the user click on ‘DISAPPROVE” an alert box is shown to user and when he click on OK then only we process the form If he click on “CANCEL” then nothing happens.
As of now i am using the following Code:
form(‘approvalForm’) {
click ‘Disapproval’
}
click ‘Cancel’
assertStatus 200
But as it clicks on Disapproval the form get’s processed at it’s own and i get an error stating that ‘Cancel’ not found. I know that i am using javascript alert() to show that pop-up box so is there any way to simulate this behavior ?
Any help will be appreciated.
regards
gaurav
Greg Bridges
April 20, 2009I tried to use BootStrap.groovy to set up some test data before the tests execute, but it appears to run the tests before BootStrap is executed. Is there a different way to accomplish this?
Marc Palmer
April 22, 2009Greg – functional tests run after bootstrap is run – G-Func builds and runs the entire app before running tests.
Marc Palmer
April 22, 2009Guarav it sounds like your onclick handling is not being called by the HtmlUnit browser, or there is some nuance with simulated alert dialogs. I will JIRA & look into it.
Marc Palmer
April 22, 2009John – that is not (yet) how file uploads are handled. The object you get when accessing a file upload field is a HtmlUnit HtmlFileInput instance. You need to set the contentType and data properties on it:
http://htmlunit.sourceforge.net/apidocs/com/gargoylesoftware/htmlunit/html/HtmlFileInput.html
Duncan Sommerville
October 12, 2009It is possible to do ‘digest authentication’ with this plugin?
I’ve hada look on HtmlUnit which talks about using ‘addCredentials()’, which sounds promising, – but how to I attach these properties when using this plugin?
Duncan Sommerville
October 12, 2009Managed to figure this out:
class MyFunctionalTests extends FunctionalTestCase {
void testMyThing() {
client.getCredentialsProvider().addCredentials(“username”,”password”)
…
}
}
Marc Palmer
October 13, 2009Duncan thanks for that – I’ll add to g-func docs online.
Javen
February 21, 2010Hi Marc,
I have two testCases in a Class, such as testCase1(),testCase2()
How can I only run one?
João Paulo
June 16, 2011Hi!
Just to help anyone that might come here looking for info on how to test file uploads, here is the code of the test that I got to work:
void testRemoveReport() {
FileWriter writer = new FileWriter(new File(“newReport.txt”));
PrintWriter out = new PrintWriter(writer);
out.println(“Blablabla”);
out.close();
writer.close();
(here i get the page where i can upload a report)
form() {
report.contentType = “text/plain”
report.data = new File(“newReport.txt”).bytes
report.valueAttribute = new File(“newReport.txt”).absolutePath
click “Submit”
}
assertContentContains “newReport.txt”
}
The only unmentioned thing here was the valueAttribute bit, where you have to set to the absolute path of your file.
Hope I can help someone!