Between unit and integration testing

 

Everyone knows that it is mandatory to implement automated software tests that run on every build, commit or on a continuous integration system to ensure the quality of your developed software and to find bugs before a user has to experience it. The most common way to do this is to implement unit tests to verify the functionality of very small-grained code units (i.e. classes). Depending on your development process, you maybe even implement these test cases based on your requirements before you implement the software itself. But in some environments and for some software components it is very time-consuming to implement a unit test for every component, especially if the unit has a lot of dependencies that have to be mocked by using a mocking framework like Mockito or EasyMock. To write a unit tests for a plugin's Manager class that uses other Manager classes from the Jive core, you have to mock all of these dependencies. Some components like DataAccessObjects (DAOs) are even harder to test because you have to find a way to mock a RDMS. To test your software "in action" (i.e. against a running RDMS), you can of course implement automated integration tests with frameworks like Selenium that run on the frontend of your application. However one drawback of this approach is that you have to rewrite tests very often with every change of the UI. If you have only little control over the system's UI (as you have when developing plugins for Jive), you might have to rewrrite the whole UI and all the tests with it with every new major version.

These drawbacks made us look for another way to test our plugins that is not too time-consuming, not that affected by Jive UI changes but can test the plugin functionality regarding the interaction with the Jive core components. This blog post describes the found solution that uses a testing framework that is implemented by and is delivered with Jive.

 

Functional testing with JiveCommunityTest

 

The core base class for implementing automated functional tests for Jive plugins is the class com.jivesoftware.util.JiveCommunityTest located in com.jivesoftware:core-test-base. The JavaDoc for this class reads as follows:

This class is an extension of jUnit's TestCase. It is designed to be used as a functional test harness. A functional test is a test which needs various system resources set up -- a database connection, config files, etc. By simply extending this class you can call any Jive APIs and be assured there is a clean database underneath the hood.

 

If you use this class as base class for your custom JUnit test, it will start up (@BeforeClass) and tear down (@AfterClass) an environment that is very close to the real jive-application:

  • Create the spring application context
  • Create an in-memory database (HSQL) or setup up the schema on an existing database like Oracle, PostgreSQL,... (see com.jivesoftware.util.bellerophon.TestDatabaseProvider for details)
  • Setup jive home directory

 

Extending the Spring application context

 

So imagine having a class CustomManager in your plugin that has dependencies to Jive's UserManager, GroupManager, TagManager, ... and now you want to create an automated test class for it.

To implement your CustomManagerTest, you just have to create a test class that is derived from JiveCommunityTest:

 

@Configurable(autowire = Autowire.BY_NAME)
@SpringContext("classpath:spring-customTestContext.xml")
public class CustomManagerTest extends com.jivesoftware.util.JiveCommunityTest {

    private CustomManager customManager;

    @Required
    public void setCustomManager(CustomManager customManager) {
        this.customManager = customManager;
    }

    @Test
    public void perform() throws Exception {
        Assert.assertTrue(customManager.works());
    }
}





 

By default, the base class starts up the Spring application context that is defined in classpath:spring-testApplicationContext.xml. As you need an extended context here (containing the configuration for the plugin's beans) you have to create a new file spring-customTestContext.xml that includes the plugin's spring.xml and Jive's spring-testApplicationContext.xml:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" [...] >
    <import resource="classpath:spring-testApplicationContext.xml" />
    <import resource="classpath:spring.xml" />
</beans>




 

You also have to ensure that the classpath is correctly set up when running the tests.

 

Extending the database schema

 

Jive offers the possibility to extend the database schema by plugins. To achieve this, you just have to describe your tables in XML in a file called schema.xml. When running a test based on JiveCommunityTest this additional schema is not automatically created by the test class. To prepare the test database for your plugin test, you can use the class com.jivesoftware.util.bellerophon.TestDatabaseProvider to obtain the database configuration and create the required schema.

 

@BeforeClass
public static void setupSchema() throws Exception {
    final DatabaseProperties props = TestDatabaseProvider.getDatabaseProperties(DatabaseTypes.HSQL);
    TestDatabaseProvider.createSchema(props, CustomManagerTest.class.getClassLoader().getResourceAsStream("schema.xml"));
}




 

By using the @BeforeClass annotation, the custom schema is created after the base class' setup is performed but before any of the tests contained in this class is executed.

It is also possible to call the createSchema() method again to prepare the database with additional test data that is defined in another XML-File.

 

Creating places, content, users with FunctionalTestUtil

 

Many test cases depend on existing users, groups, permissions, places or content in a jive application, that are not present when a clean database is set up for every test run. You can of course - as described in the section above - define another schema.xml file that creates the required jive objects, but that requires a deep knowledge of what is implemented in the jive core.

The better solution is to use the class com.jivesoftware.util.FunctionalTestUtil that provides lots of helper methods to create and link these core jive objects, for instance:

 

User user = FunctionalTestUtil.createTestUser("john.doe", "***", "john@googlemail.com");
SocialGroup socialGroup = FunctionalTestUtil.createTestSocialGroup("John's private group", user, DefaultSocialGroupType.PRIVATE);



 

Running multiple test classes in the same context

 

The start up of the environment for one test class obviously takes some seconds to minutes. To keep tests maintainable a developer will prefer to implement test cases grouped by components in multiple test classes. The more test classes you derive from JiveCommunityTest, the longer the test execution for your plugin will take. But fortunately, JiveCommunityTests offer the possibility to create a shared environment to be used by multiple test classes. To configure this mode, you just have to run your tests with an additional parameter: -Djive.test.oneContext=true

 

Conclusion

 

Implementing automated functional test cases with the described framework by Jive is a great possibility to test your developed components against the jive core components. As you nearly don't have to spend any time to mock your component's dependencies, you can focus on the actual business logic of the test case. Another advantage is the possibility to run your tests against a real RDBMS and to use a defined and clean schema and dataset for every test run.