EasyB + Grails Testing

  • Authors: null
0 vote
Dependency:
compile ":easyb:2.0.5"

 Documentation

Summary

Description

Overview

Groovy testing implementation of easyb - exposes all of the easyb functionality and an infrastructure to expose the testing functionality that is normally available only to Grails' Unit Testing framework.

The current released version of the Grails easyb plugin works only with versions 1.2 and above of Grails. This is because the Grails testing infrastructure changed significantly between 1.1 and 1.2.

See http://www.easyb.org for how to use EasyB.

You may also be interested in the easyb acceptance test plugin.

Installation

As with any Grails plug-in, the user can run the install plug-in command to install a plug-in:

grails install-plugin easyb

Where do my files go?

As with other Grails testing frameworks, tests go in your test/unit, test/integration and test/functional sub directories in the test directory. For example, if doing JAX-RS easyb testing, you can put a file called CartResource.story in test/unit and easyb will run that when it is running unit tests.

Both HTML & plain text reports are now stored in the "target/test-reports/" directory inside the grails project.

Running EasyB

After installing, users can use the following commands to run unit, integration, functional or easyb stories.
  1. Run all EasyB & Grails tests including stories, unit & integration "grails test-app"
  2. Run all easyb stories "grails test-app :easyb"
  3. Run unit test stories for easyb: "grails test-app unit:easyb"
  4. Run integration test stories for easyb: "grails test-app integration:easyb"
  5. Run single unit test story: "grails test-app ExampleService unit:easyb"
It is recommended that you suffix your stories and specifications with .story and .specification to make it more clear for others and Grails easyb when trying to run your tests. Ending in one of these suffixes will ensure you easyb tests don't clash with other test plugins.

Best Practice

It is best practice to put your tests (.story or .specification) in the same packages as the classes being tested. The grails easyb plugin will automatically match the tests with their corresponding class. For example:
  • com.bluetrainsoftware.AuthorController.story will correspond to com.bluetrainsoftware.AuthorController.groovy
  • com.bluetrainsoftware.AuthorTagLib.story will correspond to com.bluetrainsoftware.AuthorTagLib.story
In addition, controller stories will mock the controller methods & variables available in standard grails controller unit test classes (EX: renderArgs, redirectArgs). The story will include a binding variable called "controller" automatically. In the case of taglibs, a binding variable called "taglib" is included.

It is not crucial to follow this mechanism, these are just convenience methods. If you do not wish to do this, you can use:

grailsTest "controller", "NameOfControllerClass"

to have the plugin automatically mock your controller (use "taglib" for taglibs).

Using Services

There is a keyword that can be used in Specifications and Stories in the integration scope called "inject".

/**
 * introduced by Jeffrey C. Erikson
 */

scenario "we should be able to inject services", { when "requesting the author service bean", { inject "authorService" } then "author service should be callable", { authorService.doSomething(3).shouldBe "burp" } }

scenario "non existent beans should fail", { when "we have a request for a non-existent bean", { beanReq = { inject "wibble" } } then "it should fail", { ensureThrows(Exception) { beanReq.call() } } }

Controller Unit Tests

Since the EasyB plug-in leverages the Grail testing framework, writing controller unit stories work very similar to writing standard controller unit tests. Take the following controller:

class SearchController {
    def ldapService

def index = { def result = [:] if (!params.query?.trim()) { return result }

try { if(params.user) { result.searchResult = User.findByLastname(params.query, params) } else { result.searchResult = ldapService.search(params.query, params) }

} catch (Throwable t) { log.error("Error while performing search", t) result.searchResult = null result.exception = true } return result } }

In the controller above, there are four basic execution scenarios. The scenarios consists of the following:

  • No search parameter "query" is passed and the method returns an empty result
  • Search parameter is passed and search is performed
  • Search parameter is passed, the "user" search parameter is passed indicating a user only search, and search is performed
  • Search parameter is passed, exception is thrown while performing a search, an empty result is returned
These scenarios are supported in EasyB story format easily. Validation of these scenarios works the same as in standard Controller unit tests. Developers should utilize the "mockController" method to mock out request parameters and redirectArgs. Developers should also use meta-programming for mocking service & domain methods when necessary. See the SearchController story below for reference:

scenario "Search controller returns results of a ldap search", {
	given "Controller is instantiated", {
		mockController SearchController
		controller = new SearchController()

ldapService = mockFor(LdapService) ldapService.metaClass.search = { String param, Map params -> def searchResult = [:] searchResult.total = 2 searchResult.max = 10 searchResult.offset = 0 searchResult.results = [new User(name: "Test 1"), new User(name: "Test 2")] return searchResult } controller.ldapService = ldapService } when "controller receives request for index action" and "a search parameter is included", { controller.params.query = "Smith" result = controller.index() } then "controller display search page", { controller.redirectArgs.view.shouldBe null } and "controller returns a list of search results", { result.searchResult.total.shouldBe 2 result.searchResult.max.shouldBe 10 result.searchResult.results.size().shouldBe 2 } }

scenario "Search controller responds to a User search", { given "Controller is instantiated", { mockController SearchController User.metaClass.static.findByLastname = { String param, Map params -> def searchResult = [:] searchResult.total = 2 searchResult.max = 10 searchResult.offset = 0 searchResult.results = [new User(name: "Test 1"), new User(name: "Test2")] return searchResult } controller = new SearchController() } when "controller receives request for index action" and "a search parameter ‘Smith’ is included", { controller.params.query = "Smith" } and "user search parameter is included", { controller.params.user = "t" result = controller.index() } then "controller displays search page", { controller.redirectArgs.view.shouldBe null } and "controller returns a list of search results", { result.searchResult.total.shouldBe 2 result.searchResult.max.shouldBe 10 result.searchResult.results.size().shouldBe 2 } }

scenario "Search controller responds to index request without search parameter", { given "Controller is instantiated", { mockController SearchController controller = new SearchController() } when "controller receives request for index action" and "no search parameter is passed", { result = controller.index() } then "controller displays search page", { controller.redirectArgs.view.shouldBe null } and "controller should return null search results", { result.searchResult.shouldBe null } }

scenario "Search controller handles exception when performing search", { given "Controller is instantiated", { mockController SearchController controller = new SearchController()

ldapService = mockFor(LdapService) ldapService.metaClass.search = { String param, Map params -> throw new Exception("Search Exception") } controller.ldapService = ldapService } when "controller receives request for index action" and "search parameter 'Smith' is passed", { controller.params.query = "Smith" result = controller.index() } then "controller indicates exception was thrown", { result.exception.shouldBe true } and "controller should return null search results", { result.searchResult.shouldBe null } }

Documented by: Chris Cantu, Richard Vowles