Menu

GettingStartedPart3

Anonymous

Testing with Impala

Impala has a heavy focus on making it easy to practice Test Driven Development (TDD) when working with Spring applications. An important aspect of the Impala environment is in the area of testing - essentially, making them easier to write, and quicker to run.

Unit tests

For the purposes of the discussion which follows, unit tests are considered to be tests on classes without requiring a Spring application contexts. Impala imposes no restrictions on the definition and running of unit tests - they work just like in any other Java project. The interesting areas is that of integration tests.

Integration tests

The example test

The pain point in this area tends to be integration tests, for a number of reasons. Integration tests are more complicated that unit tests, because they involve more dependencies. Separating out the dependencies required for the test from those which are not can be hard at times. Also, because integration tests are slower to run, slow develop/deploy/test cycle times can be an obstacle to productivity.

Apart from the modules and their dynamic reloading capability, an important part of Impala's solution in this area is the integration testing support, in particular through the interactive test runner.

Below we have an integration test for our MessageService implementation.

package com.application.main;

... imports omitted

public class MessageIntegrationTest extends BaseIntegrationTest {

    public static void main(String[] args) {
        InteractiveTestRunner.run(MessageIntegrationTest.class);
    }

    public void testIntegration() {
        MessageService service = 
                   Impala.getBean("messageService", MessageService.class);
        System.out.println(service.getMessage());
    }

    public RootModuleDefinition getModuleDefinition() {    
        return new TestDefinitionSource("myapp-main", "myapp-module1").getModuleDefinition();
    }

}

This integration test lives in the myapp-main module in the tests source folder. The fact that the test can live in the myapp-main module is important, when we consider that the implementation for MessageService is in the module myapp-module1. It tests the behaviour of the MessageService through it's interface, rather than it's implementation.

Let's consider some aspects of the test implementation.

First, it extends BaseIntegrationTest, which simply extends JUnit TestCase and has an implementation of the JUnit setUp() method, which we will see in a moment.

The next thing to notice is the main method. The main method is used to start the interactive test runner.

The test is pretty straightforward. The pertinent bit is the use of the convenience method Impala.getBean(beanName, type) to retrieve a reference to messageService. This call effectively obtains the named bean from the Spring ApplicationContext associated with the root module.

You're probably wondering at this point how Impala gets to find out about the module constituents and hierarchy in the first place. To allow this to happen, we need to provide an implementation of ModuleDefinitionSource, which we do so in the final method shown in MessageIntegrationTest.

To complete the puzzle, we need to take a look at the superclass, BaseIntegrationTest, which has the following form:

public abstract class BaseIntegrationTest extends TestCase 
    implements ModuleDefinitionSource {

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        Impala.init(this);
    }
}

The method Impala.init() takes a parameter of type ModuleDefintionSource, hence the use of this. So what happens when init() is called? Impala examines the module hierarchy definition supplied via the ModuleDefinitionSource implementation. Impala integration tests typically themselves implement the interface ModuleDefinitionSource. It then compares it with the currently loaded module set. It will then make adjustments as appropriate. For example, it it encounters a module which has not been loaded, it will load this module. If it encounters one whose definition varies from the loaded module, the loaded module will be unloaded, and a new module loaded from the definition supplied in the ModuleDefinitionSource implementation.

This mechanism allows modules to be loaded incrementally over a test run, and only unloaded when required. This helps dramatically speed up the time taken to run a suite of integration tests compared to situations in which the full Spring context is loaded for each test.

Module level integration tests

The first integration test we described lived in the root module. In this case, it is only possible to refer to root Spring module beans via their Java interfaces. At times, it is also useful to refer in integration tests to implementation beans directly. For example, if you are testing beans which are not exported to the service registry, but are instead part of the internal implementation of a module, this can be helpful.

An example of this is provided in the form of the ProjectMessageIntegrationTest, defined in myapp-module1/tests, whose testIntegration method has the following form:

public void testIntegration() {
    MessageService service = 
             Impala.getModuleBean("module1", "messageService", MessageService.class);
    System.out.println(service.getMessage());
}

Notice that the bean reference is obtained using Impala.getModuleBean(moduleName, beanName, type). This will obtain a reference to the bean implementation, and not a proxy. The restriction, of course, is that these integration tests must be found in the in the relevant module project, or in projects corresponding to dependent modules of the module containing the bean implementation.

Running the standalone interactive client

Let's get a bit more dynamic and see what happens when we run the interactive test runner for the JUnit test class MessageIntegrationTest.

As mentioned, this is a main Java application, which can be used to interactively execute tests, reload modules, etc. Here's some example output:

Using facade class: org.impalaframework.interactive.facade.InteractiveOperationsFacade
Test class set to com.application.main.MessageIntegrationTest
Starting inactivity checker with maximum inactivity of 600 seconds
--------------------

Please enter your command text
>test
Running test testIntegration
.Hello World!

Time: 0.059

OK (1 test)


Please enter your command text
>reload
Module 'myapp-main' loaded in 0.094 seconds
Used memory: 2.0MB
Max available memory: 63.6MB


Please enter your command text
>reload module1
Module 'myapp-module1' loaded in 0.043 seconds
Used memory: 2.7MB
Max available memory: 63.6MB


Please enter your command text
>rt
Running test testIntegration
.Hello World!

Time: 0.021

OK (1 test)


Please enter your command text
>

Any of the JUnit integration tests can be run as a regular unit test in Eclipse, with a green bar showing in the Eclipse JUnit view when tests succeed.

Tests project

A feature of an Impala workspace is a single myapp-tests project. The main purpose of the myapp-tests project is to provide a single location from which all test suites can be run. For this reason, by convention, the myapp-tests project will include references to all the projects in the workspace which contain tests, as the image below shows:

The myapp-tests project can reference all projects in the workspace, it is the ideal place for defining test suites which can be run across multiple application projects, as the next code listing shows. From the tests project, find the class AllTests. This contains a suite of tests covering all the tests in the project. Run this as a regular unit test, and you will see the following:

This covers testing for now. In part four I talk about how you use Impala in a web application.


Related

Wiki: GettingStarted
Wiki: GettingStartedPart1
Wiki: GettingStartedPart2
Wiki: GettingStartedPart4
Wiki: GettingStartedPart5
Wiki: QuestionAndAnswers
Wiki: WikiHome

Discussion

  • Anonymous

    Anonymous - 2008-06-05

    Originally posted by: twi...@gmail.com

    Does the last parameter moduleNames? of SimpleModuleDefinitionSource? require the name for the module that implements the interface under test? BTW i tried to look for the framework source but so far no luck.

     
  • Anonymous

    Anonymous - 2008-06-05

    Originally posted by: philzoio...@googlemail.com

    No, you can test any interface which is visible to the test. The implementation of ModuleDefinitionSource? is simply used to define the modules to be included in the test. In your tests you can execute tests against:

    • beans loaded from one of the parent modules of the current module from which the test is being run.
    • beans loaded from the same module as the test.
      In MessageIntegrationTest, which is located in the root module, you can access beans using any interface which is visible from the root module. This allows you to use the call Impala.getBean("messageService", MessageService.class). MessageService is defined in the root module, so you can cast to it from tests run in the root module. In the equivalent test contained in the module module1, you have the additional option of accessing the module directly using the class MessageServiceImpl, using the call: Impala.getModuleBean("module1", "messageService", MessageServiceImpl.class). However, you wouldn't be able to make such a call from another child module of root, say module2. This is because MessageServiceImpl is not visible to module2 (You'd also get a compile error in Eclipse, so no need to worry about doing this inadvertently).

    However, from a test in module2, you would still be able to use the call: Impala.getBean("messageService", MessageService.class), since the MessageService interface is visible from module2 (a child of the root module where it is defined).

    In short, you can use interfaces/classes which are visible to the class loader of the current module.

    Hope this helps.

     
  • Anonymous

    Anonymous - 2008-06-05

    Originally posted by: twi...@gmail.com

    Ok thanks. BTW this guide seems to be using the snapshot version which threw exceptions when i tried to run the test class (Part 2) and StartServer class (part 3). I only got to experience the WOW!! factor when i used the 1.0M1 version.

     
  • Anonymous

    Anonymous - 2008-06-05

    Originally posted by: philzoio...@googlemail.com

    I'll take a look at the snapshot version. Note that 1.0M1 works fine apart from the workaround for Issue 17

     
  • Anonymous

    Anonymous - 2008-06-05

    Originally posted by: twi...@gmail.com

    Ok, that didn't bite me but i've just fixed it anyway.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.