Grails: Few tips for writing integration tests

05 / Nov / 2009 by Amit Jain 4 comments

Just thought I would share with you few tips or key points we should remember while writing integration test cases:

1.  Flush and clear the session object for every single test case for example

class FooTests extends GroovyTestCase {
  def sessionFactory
  void setUp() {
    sessionFactory.currentSession.flush()
    sessionFactory.currentSession.clear()
  }
}

2.  Drop and create the test database before you run integration test: Recently I found, after commiting my integration tests in the repository, some started failing and to my surprise, at the same time that failing test passed on my machine. Later I discovered it was because of some database issue. Since then I have made it a practice to drop and recreate the database again before commiting it to the repository, and then run the test cases to ensure that all test passes.

drop database <myTestDatabase>;
create database <myTestDatabase>;

Note :If you use in-memory database you may not need to follow this step.

3.  Make different test cases to test different methods or even parts of it defined in a controller or a service: for example

 void testAccountService_addSavingsAccount() {...}
 void testAccountService_addCheckingAccount() {...}
 void testAccountService_addSavingsAccount_invalidName() {...}  //to test the expected exceptions

This would make your test cases more understandable and focussed on individual task.

4.  Avoid making your test case depedent upon the data. Otherwise there is high probability that it would break if any changes were made to the data

Lets have a look at the sample code for Integration test written for the controller:

class AccountControllerTests extends GrailsUnitTestCase {
    def accountService
    def sessionFactory
    def renderMap
    def controller

    protected void setUp() {
        super.setUp()
        controller = new AccountController()
        controller.accountService = accountService
        sessionFactory.currentSession.flush()
        sessionFactory.currentSession.clear()
        AccountController.metaClass.render = {Map map ->	//This is to access model when view or template is rendered in the controller
            renderMap = map
        }
    }
   public void testSave() {
        def oldCount = Account.list().size()
        loadParams()
        controller.save()
        assertEquals "Created a new account", renderMap.model.status //accessing model values rendered with view/template
        assertEquals(oldCount + 1, Account.list().size())   //not being dependent on the data
    }

   private void loadParams() {
        def params = [routeTransit: '789456124', accountNumber: '23444', accountType: "ACH",...]
        controller.params.putAll(params)
    }
}

Hope this helped!

Cheers!
~~Amit Jain~~
amit@intelligrape.com
IntelliGrape Softwares

http://www.tothenew.com

FOUND THIS USEFUL? SHARE IT

comments (4)

  1. Amanuel Nega

    Hey there.
    I was just wondering how do you handle data dependencies between test methods? I mean if the test method need a clean database before start!

    Reply
  2. Sakuraba

    Running integration tests against an in-memory database will definetly get you in trouble. Some exceptions only happen on a specific database server and if you only test against e.g. HSQLDB you are setting yourself up for errors that are going to be recognized very very late in the dev-cycle.

    Manually dropping schemas is not an option. This needs to be automated. Let the tests agree on a shared dataset and use DBUnit to fill/empty the database before/after each TestSuite.

    Reply
  3. Kimble

    Running your tests against an in-memory database would relieve you of dropping / creating your database every time you’ve changed the database schema. At least as long as you don’t relay on any vendor specific functionality.

    Reply

Leave a comment - Sakuraba Cancel reply