Last updated by
4 years ago
Page: Grails Functional Testing, Version:8
Functional Testing Plugin
Author: Marc Palmer http://www.anyware.co.ukThis plugin is licensed under the Apache License, Version 2.0. The original code of this plugin was developed by Historic Futures Ltd. www.historicfutures.com (+) and open sourced.
Overview
This plugin provides really easy functional web testing within the existing framework of JUnit testing under grails.It is lightweight and leverages the HtmlUnit engine for simulating the client browser - without requiring any specific browser installed.This means you can:- Do easy REST functional testing just by issuing GET/POST etc calls and then inspect the result
- Do stateful functional testing of websites, including DOM inspection
- Do any of these against your application that is automatically run locally from WAR under Jetty, or against any other URL for testing production sites
Installation
Run: grails install-plugin functional-test
Usage
1) Run: grails create-functional-test HelloWorld2) Edit the generated <project>/test/functional/HelloWorldTests.groovy file3) Add code to the test methods (see reference below)Here's an example script:class TwitterTests extends functionaltestplugin.FunctionalTestCase { void testSearch() { get('http://www.twitter.com') click "Search" assertStatus 200 assertContentContains "search" form('searchForm') { q = "#grails" click "Search" } assertStatus 200 assertContentContains "#grails" } }
Functional Testing Reference
The test class has dynamic methods added by the plugin to make it easy to request content, post content, and interact with forms and page elements.URLs are resolved in the following ways:- Absolute urls are treated "as is"
- URLs that do not begin with a / are relative to the last page retrieved, or if it is the first page retrieved, relative to the value of the system property "grails.functional.test.baseURL"
- URLs that begin with / are relative to the application, or the value of grails.functional.test.baseURL system property if it is set.
method: get(uri)
This will issue a GET request to the URI which can be relative to the last page retrieved in the test method, or absolute within the application, or a full remote URL starting with http:// or https://An optional closure can be supplied that will enable you to attach query parameters to request:void testSomething() {
get('/mycontroller','myAction') {
headers['X-something-special'] = 'A-value-here' // NOTE: you can use this "method call" approach or assignment x = y
userName "marc"
email "marc@somewhere.com"
} assertContentContains "it worked"
}method: post(uri)
As get(uri) but using the HTTP POST methodmethod: put(uri)
As get(uri) but using the HTTP PUT methodmethod: delete(uri)
As get(uri) but using the HTTP DELETE methodmethod: form(name)
Obtains a reference to a form with name attribute matching the name passed to the method. You can then set or query values of fields in the form:void testSomething() {
get('/mycontroller','myAction') {
userName "marc"
// NOTE: you can use this "method call" approach or assignment x = y
email "marc@somewhere.com"
} form("userDetails") {
name = "Marc"
email = "secret@hades.com"
click "submit"
} assertContentContains "form submitted"
}form("userDetails") { name = "Marc" email = "secret@hades.com" screenName "the_unknown_guest" fields['convoluted.field.name'].value = "have to use setValue here" }
form("userDetails") { name = "Marc" country.select "uk" selects['currency.id'].select = "GBP" }
form("userDetails") { name = "Marc" agreedTsAndCs true click "submit" }
form("userDetails") { name = "Marc" radioButtons.typeOfService = "POWERUSER" click "submit" }
form("userDetails") { name = "Marc" click "send" }
form("userDetails") { name = "Marc" send.click() }
form("userDetails") { name "Marc" address { street "668 Rue des Mortes" country "The U.S. of A" } send.click() }
method: byId(elementID)
Retrieves an element from the current page by its id attribute. Returns null if there is no such element.method: byName(elementName)
Retrieves an element from the current page by its name attribute. Returns null if there is no such element. Throws and exception if there are multiple elements in the page with the same namemethod: byXPath(xpathQuery)
Retrieves the first element from the current page matching the XPath query. Returns null if there is no such element.property: page
This property is available at all times, once a page has been retrieved. It exposes the underlying HtmlUnit page object for more advanced manipulation.You can also get direct access to any forms of the page:void testSomething() {
get('/mycontroller','myAction') assertTrue page.forms['userDetails'] // Make sure form is there
}method: assertStatus <value>
Called to assert the numerical status code of the last response:void testSomething() {
get('/mycontroller','myAction') {
userName "marc"
// NOTE: you can use this "method call" approach or assignment x = y
email "marc@somewhere.com"
} assertStatus 403 // we're not logged in!
}method: assertContentContains <value>
Asserts that the content of the last response contains the text supplied, case insensitive and all whitespace ignored.assertContentContains "user profile"method: assertContentContainsStrict <value>
Asserts that the content of the last response contains the text supplied, case sensitive, whitespace matching.assertContentContainsStrict "User Profile"method: assertContent <value>
Asserts that the content of the last response equals the text supplied, case insensitive and all whitespace ignored.assertContent "<response>ok</response>"method: assertContentStrict <value>
Asserts that the content of the last response equals the text supplied, case sensitive, whitespace matching.assertContentStrict "<response>nOKn</response>n"method: assertContentType <value>
Asserts that the content type of the last response starts with the supplied string, eg assertContentType "text/html" will pass even if there is an encoding at the end.assertContentType "text/html" assertContentType "text/html; charset=utf-8"
method: assertContentTypeStrict <value>
Asserts that the content type of the last response matches the supplied string, case and whitespace matching exactlyassertContentTypeStrict "text/html; charset=UTF-8"method: assertHeader <headername>, <value>
Asserts that a response header equals the expected content, case and whitespace ignored eg:assertHeader "Cache-Control", "private, max-age="
method: assertHeaderStrict <headername>, <value>
Asserts that a response header equals exactly the expected content eg:assertHeaderStrict "Pragma", "no-cache"
method: assertHeaderContains <headername>, <value>
Asserts that a response header contains the expected content, case and whitespace ignored eg:assertHeader "Set-Cookie", "domain=.google.co.uk"
method: assertHeaderContainsStrict <headername>, <value>
Asserts that a response header contains exactly the expected content eg:assertHeaderContainsStrict "Set-Cookie", "domain=.google.co.uk"
method: assertRedirectUrl <value>
Asserts that the response included a redirect to the specified URLassertRedirectUrl "/auth/login"method: assertRedirectUrlContains <value>
Asserts that the response included a redirect that contains the specified stringassertRedirectUrlContains "?id=74"method: assertTitle <value>
Asserts that the title of the current page equals the value supplied, ignoring case and whitespacemethod: assertTitleContains <value>
Asserts that the title of the current page contains the value supplied, ignoring case and whitespacemethod: assertMeta <name>, <value>
Asserts that the meta tag of the current page with the specified name equals the value supplied, ignoring case and whitespacemethod: assertMetaContains <name>, <value>
Asserts that the meta tag of the current page with the specified name contains the value supplied, ignoring case and whitespaceRoadmap - future stuff
- Make assert variants dump out the actual value compared to in the case of failure eg "Value: 'xxxxx' did not contain 'y'"
- Add JSON and XML response parsing
- Add JSON and XML request payloads
- Monkey patch functional tests so no need to extend test class
- Support setting post BODY (not just params)
- Support asserting that an alert window pops up
- Fix HTML results and XSLT template says Unit Tests
- Custom test reports - with URL request stack (and all req params)
- Add support for assertElememtWithId and assertElememtWithClass
- Add assert variants that take message as first param
- Analyze stacktraces to find line of test that failed and highlight in reports