Showing posts with label integration testing. Show all posts
Showing posts with label integration testing. Show all posts

Sunday, February 15, 2009

Grails Integration Testing: common information

Integration tests differ from unit tests in that you have full access to the Grails environment within the test. Grails will use an in-memory HSQLDB database (or another configured data source, for instance MySQL) for integration tests and clear out all the data from the database in between each test. You have to understand that each test is wrapped into transaction which will be rolled back lately. So integration test:
  • will talk to database, so it will be slower than unit test
  • will persist data to database - make sure Your test environment database setting don't point to production or development database
A couple of helpful thoughts concerning integration tests:
  1. You have to initialize the service class yourself, there is no dynamic injection. It is the same way as it was during unit testing.
  2. If the service has other service class inside it, You have to initialize and assign it manually. Also it is the same way as it was during unit testing.
  3. Sometimes it is useful to flush session in order to persist to database immediately - else You might get weird thing like no "id" for the supposedly saved domain object or deleted objects would be not deleted. How to do that? It's very easy, please look at the simple example:
    class DocumentServiceTests extends GroovyTestCase {

    def documentService

    // Dynamic injection of session factory.
    def sessionFactory

    void setUp() {

    // You have to initialize service,
    // there is no dynamic injection.

    documentService = new DocumentService()
    }

    void testSomething() {

    // Some logic. For instance,
    // documentService.deleteDocumentById(1L).
    // But document is still not
    // deleted after execution.
    // You have to manually flush the session
    // before assertions.

    sessionFactory.currentSession.flush()
    sessionFactory.currentSession.clear()

    // Assertions.
    }

    }


That's all for now.

Tuesday, February 10, 2009

Grails combining integration and unit testing

Yesterday I faced the situation when in integration test I needed some logic to be mocked and to be executed in particular way. Therefore I asked a question about possibility of combining integration and unit testing approaches. In someone is interested - please review mentioned situation in details. Do not forget that is based on Grails Testing Plugin, so if You are new to it please review some of my previous posts.

I have FileService which has methods for saving/retrieving/deleting files in local file system. Files are distinguished by guid, and full path to the file is generated using storage root property plus mentioned guid. Storage root property is hard coded into the service (I know that is not the best solution but quiet comfortable for me). Described logic is listed below:
class FileService {

// File system's storage root. Hardcoded.
String storageRoot = "C:\\"

def retrieveFullPath(String guid) {
"${storageRoot}\\${guid}.pdf"
}

// Some other methods for
// saving/retrieving/deleting files.


}

Also I have other service called Project Service in which File Service is dynamically injected using Spring. It uses its retrieveFullPath method. Let's look more concrete:
class ProjectService {

def fileService

def retrieveProject(String guid) {
// Some logic.
def filePath = fileService.retrieveFullPath(guid)
// Some logic.
}

// Some other methods.
}

Everything looking good and I decided to write simple integration test. But faced with the first question: in my test I need test file to be stored somewhere in the test folder of the application. But when I will use it's guid, file service will retrieve full path according to storage root property and that path will be invalid and exception will be thrown. Therefore I decided to mock mentioned logic in order to retrieve real file which exists under test folder. Let's have a look at the test:

class ProjectServiceTests extends GrailsUnitTestCase {

def projectService

void setUp() {
// Do not forget to call super method at first.
super.setUp()

// Initialise the service.
// There is no dynamic injection.

projectService = new ProjectService()
}

void testRetrieveProject() {
// Loading file for test folder
// using Spring's ClassPathResource class.

def fileName =
new ClassPathResource("test/testfile.pdf")
.getFile().getPath()


// Mocking file service not to use its storage root
// and to return needed file path.
// In this case file service will return
// real path to the file in Your file system.

def fileServiceMock = mockFor(FileService)
fileServiceMock.demand.retrieveFullPath {
String guid ->
return fileName
}

// Creating a file service mock
// in project service.

projectService.fileService =
(FileService) fileServiceMock.createMock()

// Calling the target method.
projectService.retrieveProject("testfile")

// Some assertions.
}

}

That's all. In listed integration test we combined some approaches from unit testing. Hope someone found it interesting.

Saturday, February 7, 2009

Testing in Grails: common information

Grails supports the concepts of:
  1. Unit testing - testing individual methods or blocks of code without considering for surrounding infrastructure. You have to mock these methods or blocks using something like Groovy Mock or ExpandoMetaClass. Alternatively, you could using the Testing plugin. I will show all 3 approaches in future posts.
  2. Integration testing - you have full access to the Grails environment within the test. Grails will use a database (HSQLDB, MySQL or another found in Grails configuration) for integration tests and clear out all the data from the database in between each test.
  3. Functional testing - involve testing the actual running application. Grails has support for functional testing via Canoo WebTest plug-in.
Also be aware that You have to test all layers of the application:
  1. Domain entities - usually You have to write unit tests for validation process.
  2. Services - usually You have to write unit and integration tests to be sure that services are working properly.
  3. Controllers - usually You have to write unit and integration tests to be sure that controllers are working properly.
We will cover all mentioned tests on real examples for all layers in future posts.