Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

EclipseSmartHome/Development/UnitTesting

< EclipseSmartHome
Revision as of 03:40, 16 April 2014 by Dennis.nobel.itemis.de (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Testing Eclipse SmartHome

There are two different kinds of approaches for testing Eclipse SmartHome. One is to use plain JUnit tests for testing simple classes. The other is to execute JUnit tests within the OSGi environment to test OSGi services and dynamic behaviour. Both approaches are supported through a simple infrastructure, which allows to easily write and execute tests.

Test fragment

In OSGi tests are implemented in a separate fragment bundle, which host is the bundle, that should be tested. The name of the test fragment bundle should be the same as the bundle to test with a ".test" suffix. The MANIFEST.MF file must contain a Fragment-Host entry. Fragment bundles inherit all imported packages from the host bundle. In addition the fragment bundle must import the org.junit package with a minimum version of 4.0.0 specified. The following code snippet shows a manifest file of the test fragment for the org.eclipse.smarthome.core.library bundle.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tests for the Eclipse SmartHome Core Library
Bundle-SymbolicName: org.eclipse.smarthome.core.library.test
Bundle-Version: 0.7.0.qualifier
Bundle-Vendor: Eclipse.org/SmartHome
Fragment-Host: org.eclipse.smarthome.core.library
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.junit;version="4.0.0"

Tests are typically placed inside the folder src/test/java.

Unit tests

Each class inside the test folder, which has a public method with a @Test annotation will automatically be executed as a test. Inside the class one can refer to all classes from the host bundle and all imported classes. The following code snippet shows a simple JUnit test which tests the toString conversation of a PercentType.

public class PercentTypeTest {
    @Test
    public void DoubleValue() {
           PercentType pt = new PercentType("0.0001");
           assertEquals("0.0001", pt.toString());
    }
}

Using the the hamcrest matcher library is a good way to write expressive assertions. In contrast to the original assertion statements from JUnit the hamcrest matcher library allows to define the assertion in a more natural order:

PercentType pt = new PercentType("0.0001");
assertThat pt.toString(), is(equalTo("0.0001"))

To use the hamcrest library in your test project, you just have to add the following entry to the list of imported packages:

org.hamcrest;core=split

Tests can be executed from Eclipse by right-clicking the test file and clicking on Run As => JUnit Test. From maven one can execute the test with mvn test command in the folder of the test fragment bundle.

Groovy

Using the JVM language Groovy tests are very easy and efficient to write. Groovy supports mocking without any frameworks. Language features like closures, type-inference and native syntax for maps and lists allow to implement short and easy to understand tests. Thus Eclipse SmartHome comes with a out-of-the-boc-support for Groovy-testing in Eclipse and maven. Each test file which is placed under src/test/groovy will be automatically compiled and executed in Eclipse and maven. Moreover the Eclipse SmartHome Yoxos profile contains the Groovy-Eclipse-Plugin.

Even the following examples are presented in Groovy, unit and OSGi tests can also be implemented in Java. If the default mocking capabilities of Groovy do not fulfil the requirements, Groovy can also be combined with Java mocking frameworks like mockito.

OSGi-Tests

Some components of Eclipse SmartHome are heavily bound to the OSGi runtime, because they use OSGi core services like the EventAdmin or the ConfigurationAdmin. That makes it hard to test those components outside of the OSGi container. Equinox provides a possibility to execute a JUnit tests inside the OSGi environment, where the test has access to OSGi services.

Eclipse SmartHome comes with an abstract base class OSGiTest for OSGi tests. The base class sets up a bundle context and has convenience methods for registering mocks as OSGi services and the retrieval of registered OSGi services. The following Groovy test class shows how to test the ItemRegistry by providing a mocked ItemProvider.

class ItemRegistryOSGiTest extends OSGiTest {

    ItemRegistry itemRegistry
    ItemProvider itemProvider
    def ITEM_NAME = "switchItem"

    @Before
    void setUp() {
        itemRegistry = getService(ItemRegistry)
        itemProvider = [
            getItems: {[new SwitchItem(ITEM_NAME)]}, 
            addItemChangeListener: {def itemCHangeListener -> },
            removeItemChangeListener: {def itemCHangeListener -> }] as ItemProvider
    }

    @Test
    void 'assert getItems returns item from registered ItemProvider'() {

        assertThat itemRegistry.getItems().size, is(0)

        registerService itemProvider

        def items = itemRegistry.getItems()
        assertThat items.size, is(1)
        assertThat items.first().name, is(equalTo(ITEM_NAME))

        unregisterService itemProvider

        assertThat itemRegistry.getItems().size, is(0)
    }
}
   

In the setUp method the ItemRegistry OSGi service is retrieved through the method getService from the base class OSGiTest and assigned to a private variable. After it a new ItemProvider mock is created, which returns one item. The test method first checks that no item is inside the registry. Afterwards it registers the mocked ItemProvider as OSGi service with the method registerService and checks if the ItemRegistry returns one item now. At the end the mock is unregistered again.

In Eclipse the tests can be executed by right-clicking the test file and clicking on Run As => JUnit Plug-In Test. The launch config must be adapted, by selecting the bundle to test under the Plug-Ins tab and by clicking on Add Required Plug-Ins. Moreover you have to set the Auto-Start option to true. If the bundle that should be tested makes use of declarative services (has xml files in OSGI-INF folder), the bundle org.eclipse.equinox.ds must also be selected and also the required Plug-Ins of it. The Validate Plug-Ins button can be used to check if the launch config is valid. To avoid the manual selection of bundles, one can also choose all workspace and enabled target plug-ins with default Default Auto-Start set to true. The disadvantage is, that this will start all bundles, which makes the test execution really slow and will prodcue a lot of errors on the OSGi console. It is a good practice to store a launch configuration file, that launches all test cases for a test fragment.

From maven the test can be executed by calling mvn integration-test. For executing the test in maven, tycho calculates the list of depended bundles automatically from package imports. Only if there is no dependency to a bundle, the bundle must be added manually to the test execution environment. For example Eclipse SmartHome makes use of OSGi declarative services. That allows to define service components through XML files. In order to support declarative services in the test environment the according bundle must be added in the pom file within the tycho-surefire-plugin configuration section as followed:

...
<build>
    <plugins>
        <plugin>
            <groupId>org.eclipse.tycho</groupId>
            <artifactId>tycho-surefire-plugin</artifactId>
            <version>${tycho-version}</version>
            <configuration>
                <dependencies>
                    <dependency>
                        <type>eclipse-plugin</type>
                        <artifactId>org.eclipse.equinox.ds</artifactId>
                        <version>0.0.0</version>
                    </dependency>
                </dependencies>
                <bundleStartLevel>
                    <bundle>
                        <id>org.eclipse.equinox.ds</id>
                        <level>1</level>
                        <autoStart>true</autoStart>
                    </bundle>
                </bundleStartLevel>
            </configuration>
        </plugin>
    </plugins>
</build>
...
   

In the dependency definition the artifactId is the name of the required bundle, where the version can always be 0.0.0. Within the bundleStartLevel definition the start level and auto start of the depended bundles can be configured. The org.eclipse.equinox.ds bundle must have level 1 and must be started automatically.

Back to the top