Developing and running test cases with EUnit

EUnit is a framework used to write and run repeatable test cases and to create reports about the results. The framework code is in the org.eclipse.edt.eunit.runtime package.

An interactive process is available for running tests. A batch mechanism is planned to let you run tests automatically; for example, to run tests overnight. However, only the interactive process is in place at this time.

Here are details about the EUnit Framework, in an order that reflects how you can practice using it:
  1. Writing test cases
  2. Creating a test driver to run the test cases
  3. Running your test cases
  4. Reviewing the test reports
  5. Working with JavaScript asynchronous

Writing test cases

To write test cases, code an EGL library and document it in a way that provides details for display in the output report. A set of library functions implement your test cases, one function per test case.

Here is an example library with four test cases, along with a Record type that the library references:
package common;

import org.eclipse.edt.eunit.runtime.LogResult;
import org.eclipse.edt.eunit.runtime.Test;
import org.eclipse.edt.eunit.runtime.status;

/** @test
  * @description Test cases for demonstration
  * @keywords Comparisons, Skips
**/
library MyTestCases
   function test01() {@Test}
      // do processing here, as appropriate for the test case.
      a int = 100;
      b int = 50;
      success boolean = a > b;
      LogResult.assertTrue("Test for one integer greater than the other. ", success);
   end

    	
   function test02() {@Test {targetLang = [Java, JavaScript]}}
      // do processing here, as appropriate for the test case.
      a string = "first";
      b string = "last";
      LogResult.assertStringEqual("Test for two equal strings. ", a, b);
   end

   function test03() {@Test}
      // do processing here, as appropriate for the test case.
      myTrue boolean = true;
      myFalse boolean = true;
      // expected myFalse to be false
      if (myTrue==myFalse)
         LogResult.failed("Failed to distinguish between true and false. ");
      else
         LogResult.passed("Logic is correct.");
      end
   end

   function test04() {@Test}
      LogResult.skipped("Waiting to resolve bug 2525");
      return;
      // remove the previous two lines when the bug is resolved.

      // do processing here, as appropriate for the test case.
   end

   function test05() {@Test{targetLang = [Java]}}
      myException MyExceptionType;
      try     

         // do processing here, as appropriate for the test case.
         throw new MyExceptionType{message = "An unexpected exception"};

         onException (except AnyException)
            LogResult.error(except.message);
         end
      end
   end
Record MyExceptionType type Exception end 
As shown, each function has the following characteristics:
  • Is annotated with the Test annotation.
  • Takes no parameters and returns no value.
  • Invokes at least one function from the EUnit LogResult library; typically, to compare an actual value with an expected one.

    If you are comparing one record, handler, or external type to another, invoke a series of LogResult functions, one for each field value of interest.

The following LogResult functions are meant for use in test cases, rather than being primarily for use by the EUnit test framework:
  • LogResult.assertTrue, which is shown earlier in the test01 function.
  • LogResult.assertXequal, where X is a type name such as String. An example is in test02.
  • LogResult.failed, LogResult.logStdOut, and LogResult.passed, as shown in test03.
  • LogResult.skipped, as shown in test04.
  • LogResult.error, as shown in test05

For details on those functions, see the following EGL Language Reference entry: LogResult external type.

Creating a test driver to run the test cases

A test driver is a project from which you run the test cases. A test driver is language specific, including only the test cases that were targeted for a given language.

To specify the target languages for a given test case, assign values to the targetLang field of the Test annotation, as shown earlier in test02 and test05. Here is further detail:
  • The targetLang field takes a list of enumeration values of type TargetLangKind. In Eclipse IDE for EGL Web Developers, the valid values are Java and JavaScript.
  • If you do not specify the targetLang field in a test case, the effect is the same as specifying a list that includes every TargetLangKind value.
To create a test driver, do as follows:
  1. Right click on the project that contains your test cases, or on one or more packages that contain test cases, or on one or more source files that contain test cases. In this way, you decide what subset of test cases will be in the test driver.
  2. Click Generate EGL Test Driver and then click the kind of test driver that you want to create:
    • Java, in which case a project is created with a name that ends with eunit.java.
    • JavaScript, in which case a project is created with a name that ends with eunit.javascript.
    • JavaScript Asynchronous, in which case a project is created with a name that ends with eunit.javascriptasynch.

Running your test cases

To run the test cases, do as follows:
  1. In the test driver, expand the EGLSource folder and then the eunitgen package.
  2. Run all tests:
    • If the test driver is for Java, do as follows:
      1. Right click RunAllTests_pgm.egl.
      2. Click Run as > EGL Java Main Application.
    • If the test driver is for either JavaScript or JavaScript asynchronous, do as follows:
      1. Right click RunAllTests_rui.egl.
      2. Click Run as > EGL Rich UI Application.

Reviewing the test reports

To review the test reports, do as follows:
  1. Click on the name of the driver and either press F5 or right click and then click Refresh.
  2. Expand the ResultRoot folder, which includes one more subfolders that are named for the time at which you ran the test case.
  3. Access the test report summary by double clicking on the file for which the file extension is trs. The Test result summary root is displayed.
  4. That displayed page gives you several choices:
    1. To see summary information for all the test cases you ran, click on the Test Result Summary.
    2. To see summary information for all the test cases in a particular package, expand the Test Result Summary and click on the package name.
    3. To see details for all the test cases in a given library, expand the package name and click on the library name.

      The library-specific report includes a msg: title, and after that title is the content of the logged messages for that library.

    4. To return to the source code of the library, click on the hypertext link in the middle of the library-specific report.
The detailed output is as follows:


Example of a library-specific report


Working with JavaScript asynchronous

When you work with the JavaScript asynchronous option, you verify results in a callback function, one per test. The callback function has the following characteristics:
  • Wraps the assertion statements in a try block.
  • Calls TestListMgr.NextTest, which ensures that the EUnit framework runs the sequentially next test.
  • Handles exceptions by using the following helper methods from the TestListMgr library:
    • TestListMgr.caughtAnyException
    • TestListMgr.caughtFailedAssertion
    • TestListMgr.handleCallBackException

The next example demonstrates how to use the TestListMgr library, which includes additional functions that you can ignore because their purpose is to support the EUnit framework.

Consider the following Service type, which receives a list of integers and returns the average:
package server;
 
Service MyServiceType
 
   function calculate(myScores Int[] in) returns (Decimal(4,2))
      numberOfScores, i, mySum Int;
      numberOfScores = myScores.getSize();

      for (i from 1 to numberOfScores by 1)
         mySum = myScores[i] + mySum;
      end

      return(mySum/numberOfScores);
   end
end
The following test cases ensure that the service returns the expected values:
package client;

import org.eclipse.edt.eunit.runtime.AssertionFailedException;
import org.eclipse.edt.eunit.runtime.LogResult;
import org.eclipse.edt.eunit.runtime.Test;
import org.eclipse.edt.eunit.runtime.TestListMgr;
import eglx.lang.AnyException;
import server.MyServiceType;

library MyLibrary

   function test01(){@Test}
      myBindingVar httpProxy;
      myList int[] =[2, 3];

      call MyServiceType.calculate(myList) 
         using myBindingVar
         returning to theCallBack01 
         onException theExceptionHandler;
      end

   function test02(){@Test}
      myBindingVar httpProxy;
      myList int[] =[2, 3, 4];

      call MyServiceType.calculate(myList) 
         using myBindingVar 
         returning to theCallBack02
         onException theExceptionHandler;
   end

   function theCallBack01(retResult decimal(4, 2) in, http IHttp)
      index int;

      try
         LogResult.assertDecimalEqual("Problem with Test01", 2.5, retResult);

         onException(e1 AssertionFailedException)
            TestListMgr.caughtFailedAssertion(e1);

         onException(e AnyException)
            TestListMgr.caughtAnyException(e);
      end
      TestListMgr.nextTest();
   end

   function theCallBack02(retResult decimal(4, 2) in, http IHttp)
      index int;

      try
         LogResult.assertDecimalEqual("Problem with Test02", 3.0, retResult);

         onException(e1 AssertionFailedException)
            TestListMgr.caughtFailedAssertion(e1);
         onException(e AnyException)
            TestListMgr.caughtAnyException(e);
      end
      TestListMgr.nextTest();
   end

   function theExceptionHandler(exp AnyException in, http IHttp in)
      TestListMgr.handleCallBackException(exp, http);
   end
end