I have explained in one of my last posts how to measure the coverage of the test of a Java OpenOffice.org extension. These white-box tests are generally not enough to ensure the quality of a development. XUnit tests are often used by developers to automate the tests. What about unit tests for an OpenOffice.org extension? Is there any way to automate the tests of a Java extension for OpenOffice.org? To improve the quality of the COOoder extension, I wanted to create some unit tests. I will present here some basic classes to use to create easy tests.

Theoretically...

Before presenting any code snippet, I need to explain the general principle of the unit tests for OpenOffice.org. The key concept of the following is that OpenOffice.org can be launched and controlled by a remote program. A JUnit test is a normal Java program and the idea is to make it bootstrap OOo, connect to it and execute the tests. Here is a small sequence diagram of what happens:

Sequence diagram

OpenOffice.org is bootstrapped once at the beginning by the test suite and stopped when all tests are run (this part has been forgotten on the diagram). The remote OpenOffice.org context is transmitted to all the test cases which can use it to set the fixture up, run the test and tear the fixture down.

...and practically

To implement all this nice feature, I have created some base classes for the tests cases and suites. They both inherits from the JUnit corresponding classes. The classes are in the org.openoffice.coooder.test.base package in the ooo-build project:

  • Bootstrap: the OpenOffice.org Bootstrap class modified to easily bootstrap the office
  • UnoTestSuite: the test suite to use to run all the unit tests for OpenOffice.org
  • UnoTestCase: the OpenOffice.org context-aware test case. This abstract class has to be the base of all the unit tests for OpenOffice.org Java extensions.

To correctly bootstrap the office, the program folder of the OpenOffice.org installation has to be added to theLD_LIBRARY_PATH variable (PATH on windows and DYLIB_LIBRARY_PATH on MacOS). This folder has to be passed as a property to the JUnit launcher using the following JVM parameter:

-Dopenoffice.program.path=/usr/lib/openoffice/program

Now there is only to write the tests. Here is an example of test suite and a test case:

/**
 * The test case class with two tests
 */
public class DummyTest extends UnoTestCase {

    private XTextDocument mTestDoc;

    public DummyTest() {
        super();
    }

    public DummyTest(String pName) {
        super(pName);
    }

    /**
     * Fixture setup: creates an empty writer document
     */
    protected void setUp() throws Exception {
        mTestDoc = loadDocument("private:factory/swriter");
    }

    /**
     * Fixture tear down: closes the document without saving
     */
    protected void tearDown() throws Exception {
        XCloseable xCloseable = (XCloseable)UnoRuntime.queryInterface(
                XCloseable.class, mTestDoc);
        xCloseable.close(true);
    }

    /**
     * One correct test
     */
    public void testHello() {
        try {
            String hello = "Hello world!";
            mTestDoc.getText().setString(hello);

            assertEquals(hello, mTestDoc.getText().getString());
        } catch (Exception e) {
            fail("Shouldn't have thrown an exception");
        }
    }

    /**
     * One failing test
     */
    public void testFailure() {
        fail("test will fail here");
    }

    private XTextDocument loadDocument(String pUrl) throws Exception {
        XMultiComponentFactory xMngr = getContext().getServiceManager();
        Object oDesktop = xMngr.createInstanceWithContext("com.sun.star.frame.Desktop", getContext());
        XComponentLoader xLoader = (XComponentLoader)UnoRuntime.queryInterface(
                XComponentLoader.class, oDesktop);

        XComponent xDoc = xLoader.loadComponentFromURL(pUrl, "_default",
                FrameSearchFlag.ALL, new PropertyValue[0]);

        XTextDocument xTextDoc = (XTextDocument)UnoRuntime.queryInterface(XTextDocument.class, xDoc);
        return xTextDoc;
    }
}

/**
 * The test suite class.
 */
public class AllTests  {

    public static Test suite() {

        // The tests to run by the suite
        Class[] testClasses = new Class[] {
                DummyTest.class
        };

        // Create the test suite
        UnoTestSuite suite = new UnoTestSuite(testClasses);

        return suite;
    }
}