You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

Introduction

This document describes the technical aspects of the testing integrated into Dspace. It's divided in 3 main sections, one per each type of test added to Dspace, plus a section discussing the common tools used. It's intended to serve as a reference for the community so more test cases can be created.

Common Tools

There is a set of tools used by all the tests. These tools will be described in this section.

Maven

The build tool for DSpace, Maven, will also be used to run the tests. For this we will use the Surefire plugin, which allows us to launch automatically tests included in the "test" folder of the project. We also include the Surefire-reports plugin in case you are not using a Continous Integration environment that can read the output and generate the reports.

The plugin has been configured to ignore test files whose name starts with "Abstract", that way we can create a hierarchy of classes and group common elements to various tests (like certain mocks or configuration settings) in a parent class.

Tests in Maven are usually added into src/test, like in src/test/java/<package> with resources at src/test/resources.

To run the tests execute:

mvn test

The tests will also be run during a normal Maven build cycle. To skip the tests, run Maven like:

mvn package -Dmaven.test.skip=true

By default we will disable running the tests, as they might slow the compilation cycle for developers. They can be activated using the command

mvn package -Dmaven.test.skip=true

or by commenting the corresponding profile (skiptests) in the main pom.xml file, at the root of the project.

JUnit

JUnitis a testing framework for Java applications. It was one of the first testing frameworks for Java and it's a widespread use in the community. The framework simplifies the development of unit tests and the current IDE's make even easier building those tests from existing classes and running them.

Junit 4.8.1 is added as a dependency in the parent project. The dependency needs to be propagated to the subprojects that contain tests to be run.

As of JUnit 4.4, Harmcrest is included. Harmcrest is a library of matcher objects that facilitate the validation of conditions in the tests.

JMockit

JMockit is popular and powerful mocking framework. Unlike other mocking frameworks it can mock final classes and methods, static methods, constructors and other code fragments that can't be mocked using other frameworks.

JMockit 0.998 has been added to the project to provide a mocking framework to the tests.

Unit Tests

These are tests which test just how one object works. Typically test each method on an object for expected output in several situations. They are executed exclusively at the API level.

We can consider two types of classes when developing the unit tests: classes which have a dependency on the database and classes that don't. The classes that don't can be tested easily, using standard procedures and tests. Our main problem are classes tighly coupled with the database and its helper objects, like BitstreamFormat or the classes that inherit from DSpaceObject. To run the unit tests we need a database but we don't want to set up a standard PostgreSQL instance. Our decision is to use an in-memory database that will be used to emualte PostgreSQL.

To achive this we mock DatabaseManager and we replace the connector to point to our in-memory database. In this clase we also initialise the replica with the proper data.

Structure

There is a base class called "AbstractUnitTest". This class contains a series of mocks and references which are necessary to run the tests in DSpace, like mocks of the DatabaseManager object. All Unit Tests should inherit this class, located under the package "org.dspace" in the test folder of DSpace-api.

About the implementation, several objects only offer a hidden constructor and a factory method to create an instance of the object. As this factory method depends on the database, we can't rely on it to create instances of our objects. The Reflection API has been used to create the required instances for the unit tests.

DDL Utils

A Data Definition Language (DDL) is a computer language for defining data structures. This allows you, for example, to replicate the structure of one database in many other instances. We use DDLUtils asa mechanism to extract the data from PostgreSQL and repliacte it into our in-memory database.

To create the DDL files do the following steps:

  • Download DDL Utils and unzip the file
  • Download the JDBC drivers used to connect to your database and copy the libraries to the lib folder of DDLUitls
  • Create a build.xml file with the code below (change as rquired the data on the database)
    <project name="DDL" default="database-dump" basedir=".">
    
    <path id="runtime-classpath">
      <fileset dir="lib">
        <include name="**/*.jar"/>
        <include name="**/*.zip"/>
      </fileset>
    </path>
    
    <target name="database-dump" description="Dumps the database structure">
      <taskdef name="databaseToDdl"
               classname="org.apache.ddlutils.task.DatabaseToDdlTask">
        <classpath refid="runtime-classpath"/>
      </taskdef>
      <databaseToDdl modelName="DSpace" schemapattern="%" databasetype="postgresql">
        <database url="jdbc:postgresql://localhost:5432/dspace"
                  driverClassName="org.postgresql.Driver"
                  username="dspace"
                  password="dspace"/>
    
        <writeSchemaToFile outputFile="db-schema.xml"/>
        <writeDataToFile outputFile="data.xml" determineschema="true"/>
      </databaseToDdl>
    
    </target>
    </project>
    
  •  Run the main task with the command:
    > ant
    
    This will genetare two files, db-schema.xml which contains the structure and data.xml which contains the data of the tables. We will use these files to replicate the database in the in-memory database.

NOTE: HSQLDB doesn't accept columsn with autoincrement which are not PK. The DDL has been modified so all columns with autoincrement are PK

NOTE: in table community_item_count field count renamed to comm_count due to HSQL compatibility issues with the name of the column. Be aware when creating tests for the item counter script

NOTE: in table collection_item_count field count renamed to coll_count due to HSQL compatibility issues with the name of the column. Be aware when creating tests for the item counter script

In-Memory Database

DDL Utils has some limitations on the databases it can connect to. As a result, the choices of in-memory databases are a bit limited. We have decided to use HSQLDB, an open-source and very tested database that recently has released version 2. The database we will use is an in-memory instance, which has no persistence beyond the JVM process that manages it. Once shutdown, all the information will be erased, which makes it a perfect choice for unit testing.

  • Driver: org.hsqldb.jdbc.JDBCDriver
  • JDBC Connector: jdbc:hsqldb:mem:<database> (user: SA , password: <blank>)

To load the DDL created above we

note: on dbcp the validation query select 1 is not compatible with hsql, modified accordingly

Code Issues

During the development the following issues have been detected in the code, which make Unit Testing harder and impact the maintability of the code:

* Hidden dependencies. Many objects require other objects (like DatabaseManager) but the dependency is never explicitely declared or set. These dependencies should be fulfilled as parameters in the constructors or factory methods.

* Hidden constructors. It would be advisable to have public constructors in the objects and provide a Factory class that manages the instantiation of all required entities. This would facilitate testing and provide a separation of concerns, as the inclusion of the factory methods inside objects usually adds hidden dependencies (see previous point).

Refactoring would be required to fix these issues.

Integration Tests

These tests work at the API level and test the interaction of components within the system. Some examples, placing an item into a collection, or creating a new metadata schema and adding some fields. Primarily these tests operate at the API level ignoring the interface components above it.

Functional Tests

These are tests which come from user-based use cases. Such as a user wants to search DSpace to find material and download a pdf. Or something more complex like a user wants to submit their thesis to DSpace and follow it through the approval process. These are stories about how users would preform tasks and cover a wide array of components within Dspace.

Thanks

This page has been created with help from Stuart Lewis, Scott Phillips and Gareth Waller. I want to thank them all for their comments. Some information has been taken from Wikipedia to make the text more complete. I'm to blame for errors in the text.

Feel free to contribute to this page!

  • No labels