JUnit Test Guide Q&A

This document describes how to develop and run JUnit tests for the project.

It is not a tutorial on writing JUnit tests in general.

Preconditions

  • Create OMAR DemoDB: If you have not built a demo database recently it is a good idea to do so before you run unit tests. Otherwise you may have failures due to changes in demo DB.
  • Create a Test User: Make sure you have configured a test user able to publish to Registry (and correct test properties). These are the ways you can do it:
    • Run the ant target createTestUser to create the TestUser's key to client keystore and self-register the Test User, or just loadTestUser if the previous target fails because the key already exists:

      build createTestUser
    • Manually create a test user using RegistryBrowser - Java UI. Make sure the folowing properties are set to conf/jaxr-ebxml.properties. The default values are fine as far as you create the user with them. The tests will use this infomation to get your test user certificate from the client keystore:

      jaxr-ebxml.security.test.alias=testuser

      jaxr-ebxml.security.test.keypass=testuserpasswd

      jaxr-ebxml.security.storepass=ebxmlrr
  • Deploy CMS Test Webapps: Some of the CMS tests require special webapps to communicate with. Use the following command to deploy them (might require over a 100MB of disk space):

    build stop.tomcat test.unit.deploy start.tomcat

    If you're not deploying to http://localhost:8080/ then you'll have to update the URL in the accessURI attribute of every <rim:ServiceBinding> element in every SubmitObjectsRequest_WebServices.xml file under misc/samples/cms to reflect the proper endpoint address.

Rebuilding Demo Database

Many of the regression test assume that you have certain demo data in your database. All such assumed data is loaded when you load the demoDB databases.

The following steps describe how to rebuild demodb:

  1. Shutdown tomcat
  2. Run cleandb: build.sh cleandb
  3. Run createDemoDB: build.sh createDemoDB
  4. Restart your tomcat server
  5. Even if you have performed user registration before you must re-register as explained in Configure the Test User

NOTE: Databases running in embedded mode, like Derby (default) and HSQLDB are not very practical for unit tests because server tests and client tests in local mode have to be run with application server down, while other client tests require the server to be up.

We recommend that you either use one of the external supported databases (PostgreSQL, for instance) or use HSQLDB in server mode. In case you opt for HSQLDB, switch the relevant properties in build.properties (see configuring database connection properties) and redeploy the aplication, then use the following commands to manage the database.

  • build.sh hsqldb.start: Starts the database server.
  • build.sh hsqldb.stop: Stops the database server.
  • build.sh hsqldb.manager: Starts a HSQLDB Manager GUI, useful for testing SQL queries directly to our database.

Running all unit tests automatically

To run all unit tests automatically, use a database not in embedded mode and do the following steps:

  1. cd <omar>
  2. build test.unit
  3. Look at test results by opening the following file in a web browser:

    <omar>/build/reports/unit/html/index.html

    (or see the directory name in the end of the output from last command). The results currently should be over 95% success rate

Alternatively, you can use one of the following commands:

  • build test.unit.local: Run all unit tests, on local mode (requires Tomcat down for embedded databases).
  • build test.unit.client: Run all client tests, on remote mode (requires Tomcat up).
  • build test.unit.server: Run all server tests (requires Tomcat down for embedded databases).

NOTE: after the last test has finished our script will render some HTML pages to report the results. That operation might be time and memory consuming. Use the ANT_OPTS env variable to increase JVM Max Heap Size (memory):

  • Linux: export ANT_OPTS="-Xmx256M $ANT_OPTS"
  • Windows: set ANT_OPTS="-Xmx256M %ANT_OPTS%"

Also, you do not have to wait for the report to finish generating to start looking at the results in XML format, as described in the following sections (see handleing test failures).

Running a single test

To run a single test class do the following steps:

  • cd <omar>
  • #Replace test class with the actual test class you wish to test

    ./build.sh test.single -Dtest='org.freebxml.omar.client.xml.registry.infomodel.InternationStringImplTest'

After the last test has finished running and displays its results, the script will generate an HTML report of the test. This rendering might take long and you do not have to wait for the report to finish generating to start looking at errors and failures as decsribed in the next section.

Test Failure and Errors: What To Do

Ideally we should have no tests failures or errors: 100% sucess!! Currently some tests are still failing and we need to further investigate why, being the success rate now close to 98% sucess.

If your sucess rate is much lower than that, note that many tests will fail if the test user is not properly created/configured. This is often the case after having run a createDemoDB. In such cases, check from Preconditions how to do the test user registration.

Some of our tests such as ApelonJAXRTest fail due to some history from past runs of the test. The best way to deal with these is to try rerunning the test. If it still fails then try running the test after cleandb and createDemoDB and new user registration.

When a test fails you need to look at the file produced by the test:

build/reports/unit/TEST-*.xml

for errors and failures using string search for "<error>" and "<failure>". You can also check the corresponding HTML if it has been rendered.

You then look back at the test code to see what may be broke and why.

Writing JUnit Tests

This section describes how to write JUnit tests in the context of the freebXML Registry project. It does is not a general purpose tutorial on what JUnit is and hoe to write JUnit tests in general.

When To Write Tests

A JUnit test should be created or updated:

  • Whenever you add new features to the code base. Feature development and test development should occur side-by-side.
  • When you are fixing a bug. The new test should be run against code before the fix to duplicate the bug (and fail). It should then be run after the fix to verify that the failure is gone and the test now passes.

Server Side Vs. Client Side Test

Our source code is organized by client, server and common code under packages org.freebxml.omar.client, org.freebxml.omar.server and org.freebxml.omar.common. Our JUnit tests are organized similarly.

Server side tests do not use the JAXR API while client side tests do.

One SHOULD write server side tests when the code being developed / tested is server side functionality. The reason is that with server side tests you do not have to deploy the server code each time you make a code change.

One MUST write client side tests when the code being developed / tested is client side functionality such as JAXR provider, Registry Browser Java or Web UI.

Client Side Tests in Local Mode

The client side works either in local mode (Registry Browser Web UI) or remotely (regular JAXR application, Registry Browser Java UI). By default the client tests will run in remote mode, but you should also test the functionality in local mode. For that, you have these options:

  • build test.unit.local: Run all unit tests, on local mode (requires Tomcat down for embedded databases).
  • build test.unit.local: Run all client tests, on local mode (requires Tomcat down for embedded databases).
  • build test.single.local -Dtest=<testClassName>: Run a single test, on local mode (requires Tomcat down for embedded databases).
  • build debug.single.local -Dtest=<testClassName>: Debug a single test, on local mode (requires Tomcat down for embedded databases).

Test Class Naming, Package and Location in CVS

Typically, a JUnit test Class is created to test a specific class, referred to as Class Under Test (CUT) in the project code under the <omar>/src directory. The JUnit class should have the exact same package name as the class it is testing. However, its classname should have the suffix "Test" added to it.

For example, the src class org.freebxml.omar.server.query.QueryManagerImpl is in the file <omar>/src/java/org/freebxml/omar/server/query/QueryManagerImpl.java while its JUnit test class org.freebxml.omar.server.query.QueryManagerImplTest is in the file <omar>/test/org/freebxml/omar/server/query/QueryManagerImplTest.java

Test Method Naming and Selection

Typically you add a test method for each public method that is beyond the complexity of simple accessor methods.

The name of the test method must start with the prefix "test". If it is testing a specific method in the CUT then it should use the prefix "test" followed by the name of the Method Under Test (MUT) where the MUT has been converted to upper-camel-case. The overall method name is lower-camel-case.

For example, the class org.freebxml.omar.server.query.QueryManagerImpl has a method submitAdhocQuery then the corresponding test method in org.freebxml.omar.server.query.QueryManagerImplTest is testSubmitAdhocQuery.

Templates For New Test (simple examples)

JUnit tests for the org.freebxml.omar.server package and the org.freebxml.omar.client differ slightly. Please use the following test programs as templates for your new test programs:

  • client Test: <omar>/test/org/freebxml/omar/client/xml/registry/infomodel/ServiceTest.java (note file should have been named ServiceImplTest)

  • server Test:
    • Query sample: <omar>/test/org/freebxml/omar/server/query/QueryManagerImplTest.java
    • LifeCycleManager sample:<omar>/test/org/freebxml/omar/server/query/LifeCycleManagerImplTest.java (See how to submit as registryOperator user)
  • common Test: <omar>/test/org/freebxml/omar/server/query/QueryManagerImplTest.java

When writing a new test it is suggested that you clone an existing test program from above depending upon whether your program is client, server or common test.

If you use an IDE then please make sure that generated program follows the appropriate template even if it requires some manual modification.

Thing to Do

  • Make test code simple to follow and well documented.
  • Make each testXXX method fine-grained such that each testXXX method does a very small test. This makes debugging problems easier.
  • Avoid interdependencies between testXXX methods as it makes tests more complex to understand and maintain.
  • Use id attribute instead of object name to identify objects for later retrieval or reference. This avoids leftover objects from previous runs from interfering with new runs of the test.
  • To generate UUID use:

    org.freebxml.omar.common.Utility.getInstance().createId()
  • Use BindingUtility.rimFac, queryFac etc. instead of new xxxImpl() where xxxIml is a JAXB generated impl class. JAXB Impl classes should never be used in code.
  • Clean up after your tests: make the test code attempt to remove any objects or repository items that it added to the registry. This avoids clogging up the registry with objects that will never be used again and which will just make subsequent tests run slower.

Things to Avoid

  • Avoid complexity in tests. Remember that when a test method fails, someone has to figure out what the test method is testing and why it is failing.
  • Avoid writing test methods or tests that have inter-dependencies.
  • When writing an assertEquals call make sure that expected value is the second parameter and found value is the third parameter. Don't have them backwards.
  • When writing JAXR API client tests, avoid use of BusinessLifeCycleManager and BusinessQueryManager as they are written more for UDDI support. Use LifeCycleManager, QueryManager and DeclarativeQueryManager instead.
  • Avoid testing classes in the test tree that don't have any test methods by adding an <exclude> element for those classes in the test.unit.run target in build-compile.xml.

Debugging Test Results Errors and Failure

If a test shows errors and failures (non-zero count) then using folowing steps to debug the problems:

  • Look at the XML file for the test:

    Naming convention: build/reports/unit/TEST-<package qualified test Class name>.xml

    Example: build/reports/unit/TEST-org.freebxml.omar.client.xml.registry.ApelonJAXRTest.xml

  • Typically you should look at errors first as they indicate a definite bug in the code:
    • search from beginning of file for "<error>" and investigate the error based on stack trace
    • Debug the error in an IDE like NetBeans using the debugger (It is essential to be able to debug in a debugger). See the debugging page for information on debugging OMAR.
  • Fix errors and then rerun test until no more errors
  • Now investigate the failures following same technique