One way to develop functional tests under Grails is to keep the app running while you launch tests from another jvm and use -baseUrl to target the remote jvm. You can do this from the command line or use the Functional Test Development plugin. Another way is to keep the app running and run the tests from within an IDE like IntelliJ; a bonus is that you can run individual tests/features which is great if you have a fairly large spec.
Whichever way you choose, you're going to need to setup data on the target jvm. On my last project we used fixture controllers that we would call from our functional tests but these grew into thousands of lines because re-use after a while became difficult. A better way to do it is to bundle the test data with the actual test which is what the Remote Control plugin allows us to do - the plugin documentation has a better explanation of what I mean.
With all that said, what I want to be able to do is:
- Run my app up
- Create my data in my setup block as though I was in an integration test.
- Right click on the test from intellij and hit run (or use debug and step through line by line)
public ParentSpec extends GebSpec {and then your tests:
String getBaseUrl() {
"http://localhost:8080/monkeytails/"
}
def setupSpec() {
if (BuildSettingsHolder.getSettings() == null) {
BuildSettings bs = new BuildSettings()
bs.metaClass.getFunctionalTestBaseUrl = { getBaseUrl() }
BuildSettingsHolder.setSettings(bs)
}
}
def cleanupSpec() {
resetDatabase()
}
void resetDatabase() {
def remote = new RemoteControl()
remote {
User.list().each{ it.delete(flush:true) }
// Other teardown stuff goes here
// ...
return true
}
}
}
def "Test something"() {It's handy to have firebug in the firefox that webdriver opens so use the profile manager to create a profile called test and then change your IntelliJ JUnit Run configuration VM Parameters to include '-Dgeb.driver=firefox -Dwebdriver.firefox.profile=test'
setup:
def remote = new RemoteControl()
remote {
User user = new User()
user.save(flush: true)
return true //have to return something serializable
}
.
.
}
Now you when you right click and run your test, IntelliJ will first compile everything and hopefully run your test. I say hopefully because I have a bunch of inline plugins so I have to exclude them from the compile path and hope for the best. Setting breakpoints and choosing debug lets you step through your test and let you can use firebug within the browser.
There's a few things to be aware of. Groovy's property setters don't seem to work so you can't do stuff like user.username, instead you have to use the setter or if there isn't one you can use setProperty(). Spread operators don't work too well either, so you can't do User.list()*.delete().
Update: forgot to mention that you should run the app in test mode or Remote Control will not set up its listener unlesss you set remoteControl.enabled to true in your app config
3 comments:
All good, except the remote control plugin falls down when you start using DSL's inside your remote block. We have been using the fixtures plugin to build up test data. At the last time of testing this didn't play well, which is a shame. Rob and I managed to make a remote version of the fixtures plugin but this meant putting the fixture block in a string to stop it evaluating. Not nice. We still haven't reached a conclusion on which is the lesser of evils. Long setup blocks newing up domain objects with the remote plugin or our remote fixture plugin. Sigh. ;-)
Another thing I just noticed, we only over rode the getBaseUrl() method with the following
String getBaseUrl() {
super.getBaseUrl() ?:
ConfigurationHolder.config?.grails?.serverURL ?: "http://localhost:8080"
}
That was enough to let us run tests in the IDE and at the command line.
Post a Comment