AgileApps Support Wiki Pre Release

Unit Test Framework

From AgileApps Support Wiki

In the AgileApps Cloud platform, the Unit Test Framework makes it possible to define tests in your classes, and then run them on one class at a time, or on all of your classes at once.

About Unit Testing

Unit Testing is the key that unlocks the door to programming nirvana. As your suite of unit tests grow, bugs that would have been exceptionally deep suddenly become extraordinarily shallow--because some test, somewhere, surfaces the "violation of expectations" that occurs when a method deep in the belly of the code returns an unexpected value. Without a comprehensive test suite, you find yourself laboriously tracing your way down a chain of calls to find it. But with a comprehensive test suite, debugging frenzies are a thing of the past; Because one of your unit tests inevitably takes you right to the source of the problem, where there are only a few lines of code to debug.

In addition, as Martin Fowler points out so well, a comprehensive suite of unit tests makes it possible to refactor with impunity. If that new feature you want to add requires drastic reorganization of the code, no worries. Your comprehensive test suite makes sure that after refactoring, everything works in exactly the same way it did before.

Note:
Fowler's particular genius lay in pointing out that you what seems like a very large refactoring can be broken down into a number of much smaller steps. So you do a small amount of refactoring, and then run the test suite to make sure everything is still working. If everything succeeds, you do a bit more of the refactoring. If it isn't working, you fix things, or even back out your changes and start over. At every stage of the process, everything works exactly the way it did before. By the end of the process, it's possible to have made very large changes, and at the same time know that you haven't broken anything.

Of course, you only get a comprehensive test suite if you write your tests as you go along, side by side with your code. In fact, it's a generally considered a good idea to write tests before you write your code. That way, (a) You're guaranteed to have them and (b) You know with certainty when your code is working the way you expect it to.

In a way, unit tests also provide highly detailed, totally accurate documentation of your APIs. In some shops, in fact, you're not even allowed to commit code to the shared repository unless it is accompanied by the tests which describe the behavior, and at the same guarantee compliance with the description.

Finally, to make it all work, you need an automated test harness that will run your tests and make it possible to quickly zero in on any that failed. That is the goal of the Unit Test Framework.

Warn.png

Important:
Changes made to a record in a unit test do not persist. That way, the results you get do not depend on the order in which you run them. That's great for proper unit tests, but there are times when you need to try things out programatically, and then inspect the results interactively. The way to do that is to create an Event Rule that invokes the method.

Learn more: Java Debugging Tips#Invoking Methods

Creating Unit Tests

Any method in a Java class can be a test method, as long as it is tagged with the @TestMethod annotation. Within the test method, use assert statements like this one to compare expected results to actual results: RunTest.assertEquals(expected, actual).

Here's a template for a test method:

/**
 * javadoc comment
 */
@TestMethod
public void testSomeBehavior() throws Exception
{
    String expect = "It's working!";         
    String actual = someBehavior();          // Invoke the method you're testing

    RunTest.assertEquals(expect, actual);
}

See also: Code Sample:Test of Search using Java API

Thumbsup.gif

Tip: Give your test methods meaningful names that tell what the test was trying to do. That way, when you're reading a report that identifies a failure, the name will tell you a lot. For example: testTwoPlusTwoEqualsFour.

Considerations
  • A single test method can contain multiple assertions.
  • Each successful assertion adds to the success count and the count of total tests.
  • A test method may contain no assertions at all. In that case, it runs to completion, but the test is not counted as a success.
  • A test may fail either because an exception occurs, or because an assertion fails.
  • In either case, the message is recorded. (For an exception, a stack trace is also recorded.)
  • Whether an assertion succeeds or fails, the method continues running. It is only interrupted by an exception.
  • If multiple assertions fail, all of the failure messages are reported.
  • If one or more assertions fail, and then an exception occurs, all of the messages are reported, along with the exception.
  • The test method (testSomeBehavior, above) must be public. If it isn't, an IllegalAccessException occurs when the @TestMethod annotation causes the Unit Test Framework to attempt execution.
  • The RunTest.assertEquals() method takes Strings as arguments (and only Strings).

Warn.png

Important:
When running tests, the UI is never affected. So your tests always run to completion without pausing for user interactions, regardless of the code contained in the executed methods. These cases in particular are executed without having any visible effect in the UI:

  • Functions.showMessage - Ordinarily brings up a dialog.
  • setTargetPage in a controller - Ordinarily specifies the next page the user will see.
  • Any request sent from a controller - Ordinarily specifies the controller code or JSP page that will be visited next.

Sample Tests

Here's a class with a few sample tests, to help you get started:

package com.platform.acme.demo;

import com.platform.api.*;

public class MyClass
{
  
   @TestMethod
   public void test1_string() throws Exception
   {
      String expect = "yes";        
      String actual = "yes";
      RunTest.assertEquals(expect, actual);
   }

   @TestMethod
   public void test2_int() throws Exception
   {
      // Convert the ints to strings
      String expect = ""+1;        
      String actual = ""+1;
      RunTest.assertEquals(expect, actual);
    }

   @TestMethod
   public void test3_boolean() throws Exception
   {
      // Convert the booleans to strings
      String expect = ""+true;        
      String actual = ""+true;
      RunTest.assertEquals(expect, actual);
   }

   @TestMethod
   public void test4_fail() throws Exception
   {
      String expect = "yes";        
      String actual = "no";
      RunTest.assertEquals(expect, actual);
   }
}

Running Unit Tests on a Class

To run tests for a class

  1. Go to the list of Classes
    GearIcon.png > Customization > Developer Resources > Classes
  2. Select a class
  3. Click Run Tests
    All tests defined in the class are run. The results page appears.

Test Results

This page shows the results of running the tests defined in a class.

Summary

  • Tested Class - The class that was tested.
  • Tests Run - The total number of test methods that were executed.
  • Test Failures - Tests that failed, either because an assertion failed or because an exception occurred.
  • Tests Succeeded - A count of the number of assertions that succeeded. If a single test method makes 2 assertions, and each succeeds, it counts as 2 successful tests.
  • Total Time (ms) - The total amount of time that was taken to execute the tests, in milliseconds.

Notepad.png

Note: Tests that run to completion without making any assertions are not counted.

Failed Methods

This section shows the tests that failed, either because an exception occurred or because an assertion statement turned out to be false. The error message is displayed in either case. If there was an exception, a stack trace is shown, as well.


Running Unit Tests for All Classes

To run tests for all classes

  1. Go to the list of Classes
    GearIcon.png > Customization > Developer Resources > Classes
  2. Click Run All Tests
    Tests are run for all classes. The results page appears.

All Test Results

This page shows the results of running the tests defined in all classes.

Column Headings

  • Tested Class - The class that was tested.
  • Tests Run - The total number of test methods that were executed.
  • Test Failures - Tests that failed, either because an assertion failed or because an exception occurred.
  • Tests Succeeded - A count of the number of assertions that succeeded. If a single test method makes 2 assertions, and each succeeds, it counts as 2 successful tests.
  • Total Time (ms) - The total amount of time that was taken to execute the tests, in milliseconds.

Notepad.png

Note: Tests that run to completion without making any assertions are not counted.