Unitee Testing Framework


by Ishai Asa
Development
Overview
Strategy
Vision
Versions
Licensing
Status
TODO
Download
API documentation
Running

Development

Design: Ishai Asa (ishai@isdn.net.il)
Programming: Oded Blayer (odedblayer@hotmail.com) , Ishai Asa

Overview

Unitee was a project I initiated in order to provide simple , yet effective unit (and other) testing capabilities to Java developers.

What Unitee stands for? Unitee is a combination of
* Unit - for unit testing , and twists of
* Unity - for united set of tests , for all sorts of levels (unit , system , etc.) , and
* Tea - on the weight of Java

Why Java?
Java was the major target platform because:
* It was the main development language I worked with in the last years
* Java classes could be most easily loaded on the fly (although COM could too , and C/C++ code *could* be created with some overhead)
* Java is the language Unitee is best implemented with.

Why Unitee? or: Requirements , Features
Major requirements for Unitee:
* Test cases are as simple as possible
* Test cases require as little knowledge of the Unitee framework as possible
* Test cases could be configured with different test parameters (e.g. values are not hard coded in the test)
* Test cases can test they have all parameters they need
* Extensible framework - Unitee can be extended , for example , to test COM components.

And by that provide a replacement for the overly complex and rigid JUnit.

Strategy

From the begining my strategy was (and still is) to build a tool and a framework to building generic tests , with specific data to Java classes. Being so close to build a testing tool , there was no reason to limit it to unit testing. On the contrary , Unitee is generic , and as so , one can build integration , subsystem and system testing with it.
Even the programming language barier could be washed away (with some help from my friends... ;-) ) , so C/C++/COM/CORBA components could be tested too. Essentialy , anything with an interface can be tested , so seeing oracle stored procedures test capabilities is not guarenteed (or scheduled) , but it is possible!

Talking about the 3 other kinds of testing besides unit , may have got you thinking: who the hell is doing those? Usualy those are the developers themselves , but sometimes its the folks from QA. Unitee is aimed to provide their needs too. Version 1.0.0 is stable , but Unitee requires more work to make it usable by developers who need GUIs , and QA people.

Building around the stabilized base framework of unitee , applications (GUIs) can now emerge , while using the existing minimum (UniteeClient).

Vision

The vision of the project is a derivation of both the requirements and the strategy. I strarted the project by first considering what a test case will look like (that look even got improved during development).

The process of testing a software component could be difficult enough , so adding more difficulties will result in the opposite effect. Examples: A TestCase implementor
* have more trouble to create test cases of his/her own (e.g a programs with a main())
* have more code to write and debug if using JUnit
* have a lot of trouble passing parameters around
* cannot reuse tests

Therefore , the TestCase implementor would be motivated to use the framework in order to:
* write less code , especialy when testing for different parameters is required
* debug less user written code , and debug no framework code

Here is the hello world test case:

 
public class HelloWorldTest implements TestCase {
    public void init(TestParameters params) throws ParameterException {}

    public void test(PrintStream output) throws Throwable {
        output.println("Hello world!");
        }
   }

Thats it!
A simple TestCase that actually does something will look like:
public class MyTest implements TestCase {
    private String stringParam;

    public void init(TestParameters params) throws ParameterException {
        stringParam = params.getString("myParameterName");
        }

    public void test(PrintStream output) throws Throwable {
        MyComponent mc = new MyComponent();
        output.println("mc.method1() returned:"+mc.method1());
        }
   }
 

When testing a single class method (the class that is under testing) is becoming a complex mission , inheritence and abstract classes could be used:
 
public abstract class MyTest implements TestCase {
    private String stringParam;
    private long longParam;

    public void init(TestParameters params) throws ParameterException {
        stringParam = params.getString("myParameterName");
        longParam = params.getLong("myLongParam");
        }

    public void test(PrintStream output) throws Throwable {
        // the logic for building and initializing the component is here...
        MyComponent mc = new MyComponent(stringParam);
        mc.callAnotherMethod(longParam);

        test(mc , output);
        }

    public abstract void test(MyComponent mc , PrintStream output) throws Throwable;
    }
 

public class MyMethod1Test extends MyTest {
    public void test(MyComponent mc , PrintStream output) throws Throwable {
        // do some more initializations , and finally:
        mc.method1();
        }
    }


Every exception thrown by the tested component or the test case component will propogate into the framework that will realize that the test has failed.

Versions

Version Content Status
1.0.0 Initial Release. 
Includes UniteeClient which is a command prompt client application to run Unitee tests. All tests run on the local machine.
Done - February 2001
1.1.0 UniteeServer 
enables running Java tests on a server , connecting from a client Java machine
Done - August 2001
1.2.0 UniteeSwing (I like that name!)
GUI frontend to Unitee written in JFC/Swing
Requirement gathering. Needs extensive knowledge in Swing.
1.3.0 UniteeWeb
Enables running (and running only) tests from a web browser.
The code already exist today , but it is immature , also it works on IE 5.0 only. 
Requirements exist , still gatering.
Working prototype exists.
1.4.0 + Adding some more soffisticated capabilities , like: code generation of TestCases to test a class's public interface. 
Adding testing capabilities to other languages , depending on the amount of the incoming requests.
N/A

Licensing

Currently the project is a Free Software. Generally , you can use it whenever you want , in both free software projects , or in commercial ones.
When developers volunteer to the project , licensing will change to GPL.

Status

February 8th , 2001:
* version 1.0.0 is released
* Looking for developers to take the project on.

Todo

Download

* Unitee sources and classes v1.1a1

You will also need Sun's XML parser code that is provided here for your convenience , and is usually provided as part of the JSWDK package , in a file named xml.jar. See http://java.sun.com/products/servlet/download.html. You will have to add this JAR file to your classpath , or to specify it as part of the java command.

API Documentation

The most important classes for the user are the following three:
* TestCase
* TestParameters
* Assert
Here is the complete API documentation of Unitee.

Running

Overview

All Test information (test suites , test cases , and test data) is saved in a file named "tests.xml" in the current directory where unitee is run from. Unitee will create the file if it does not exist. The topmost test suite in Unitee is named "root". Underneath will reside all of your test cases & suites. I use the following hierarchy of tests:
root
  application/framework tests
    subsystem/package tests
      class tests
        method tests

where most of the tests cases would naturally go to the method scope , and there are more and more test suites as you go higher the hierarchy. But that's just a recommandation; Unitee does not impose such restrictions.

Issue the command:

java unitee.client.UniteeClient <Parameters...>
or without parameters to get the help. It is advisable to alias the command above , using:
alias unitee='java unitee.client.UniteeClient'
or writing a batch file (or a script) to do the work.

Adding A Suite

To add a new test suite named "hello" under the "root" suite issue the command:
unitee -s hello root

Adding A Test Case

In order to add a test case , you will first have to have a parent test suite (it's "root/hello" in this case). The following command adds a new test case without parameters:
unitee -c SimpleHello test.unitee.HelloWorldTest root/hello
where "SimpleHello" is the name of this test case , "test.unitee.HelloWorldTest" is the Java class name , and "root/hello" is the test suite underneath SimpleHello will reside.

Printing the Test Tree

Simply issue the command:
unitee --tree
If you followed the previous two sections , the output should look like:
TestSuite root
   TestSuite hello
      TestCase "SimpleHello"
         class: test.unitee.HelloWorldTest
   end of TestSuite.
end of TestSuite.

Running a Test

Now that we have set our test data , it is time to actually run the test:
unitee root/hello/SimpleHello
And the output will be:
STARTING TEST "root/hello/SimpleHello"
Hello world!
TEST "root/hello/SimpleHello" has finished successfuly,
Time: 2
Test summary:
root/hello/SimpleHello succeeded.
As you may have noticed , Unitee has printed a summary of all tests (in this case it was only one) that succeeded and those who did not. This is very important when running test suites with several test suites and test cases.

Parameterized Tests (Test Data)

Adding test data to test cases and test suites is very simple and can be done when creating a new test , or while maintaining one.
To create a test case with parameters:
unitee -p -c Parameterized test.unitee.ParameterizedTest root/hello
Unitee will add the test case and prompt for parameter names and values:
choose action (a - add,r - remove,e - edit,q - quit): a
parameter name: longParam
parameter's class: ENTER
possible classes are l - long, d - double, s - String, b - byte[], p - password
parameter's class: l
parameter's value: 58
choose action (a - add,r - remove,e - edit,q - quit): q
The code for the test looks like this:
public class ParameterizedTest implements TestCase {
    long longParam;
    String stringParam;

    public void init(TestParameters params) throws ParameterException {
        longParam = params.getLong("longParam");
        stringParam = params.getString("myString");
        }

    public void test(PrintStream output) throws Throwable {
        output.println("the long value is:"+longParam);
        output.println("the string is:"+stringParam);
        }
    }
We will check again on the tree to see the changes:
TestSuite root
   TestSuite hello
      TestCase "SimpleHello"
      class: test.unitee.HelloWorldTest
      TestCase "Parameterized"
      class: test.unitee.ParameterizedTest
         long parameter "longParam": 58
   end of TestSuite.
end of TestSuite.
However , when trying to run the test Unitee will find out that there is a parameter missing:
unitee root/hello/Parameterized
missing parameter "myString" of type String
We will add this parameter , by using:
unitee -p root/hello/Parameterized

choose action (a - add,r - remove,e - edit,q - quit): a
parameter name: myString
parameter's class: s
parameter's value: blah blah blah
choose action (a - add,r - remove,e - edit,q - quit): q

Now that we will try to run the test again we will get the following output:
STARTING TEST "root/hello/Parameterized"
the long value is:58
the string is:blah blah blah
TEST "root/hello/Parameterized" has finished successfuly,
Time: 4
Test summary:
root/hello/Parameterized succeeded.
The same semantics is used to add test parameters to test suites. Parameters of test suites propagate to the child test cases and test suites , so if a certain parameter is being used by several test cases or test suites under one test suite , it can be declared under the relevant topmost test suite to avoid duplication.

So , in the example above , if all test cases under "hello" suite needed the parameter "helloParameter" , we could have added it by using:

unitee -p root/hello

choose action (a - add,r - remove,e - edit,q - quit): a
parameter name: helloParameter
parameter's class: s
parameter's value: my hello string
choose action (a - add,r - remove,e - edit,q - quit): a

Unitee server

UniteeServer is being run by just issuing the command:
java unitee.server.UniteeServer

Clients can connect to that server by adding "--server <server-ip-addr>" to each command:

unitee -p root/hello/Parameterized --server localhost