On this page:
Beginner’s option 1: use the main method in tester.Main
Beginner’s option 2: implement the IExamples interface
Using Annotations to define test classes and methods
Using Tester’s static methods to select reports
Version: 5.2.1

Running Tests

This page describes how to run tests defined in one or more example classes.

The tester provides several ways in which the user can invoke the evaluation of all test cases defined in a given class. Where you place your tests and how much control you have over the testing process is entirely dependent on which style you choose. The only hook the tester needs is for any class that defines test methods to also define the default constructor (it can be private.)

The simplest way to define and evaluate all tests is to place all test methods (with the needed data definitions) into the Examples class and make sure the name of every test method starts with test. The variety of ways for defining tests, evaluating them, and reporting the results is described in separate sub-sections of this page.

Beginner’s option 1: use the main method in tester.Main

The tester library includes a class #tt{tester.Main} that defines the start of the program, i.e. it defines the method

public static void main(String[] args)

that starts running the program.

This is the primary way for a novice user to run the tests. If the sample data and all test methods are defined in the class named Examples the programmer just needs to declare that the program’s public static void main(String[] args) method can be found in the class tester.Main.

In Eclipse this is done by defining a Run Configuration, in other IDEs one needs to set up the project parameters in a similar way. Please, see the Select Reports section for instructions relevant for the BlueJ and DrJava users.

The *tt{main} method in the class tester.Main starts the test run by looking for the class or classes that contain test methods. It starts by looking for the class named Examples.

The tester finds all methods in that class with names that start with test. Those methods are then run as the test methods. Note that these methods must take a Tester object as an argument; not doing so will cause the program to throw an error.

Note: The order in which the test methods are invoked is not specified. This is especially important when designing tests in the presence of mutation (effects).

_________________________________________________________________________________

The tester.Main class also looks for other ways that programmer may have used to define tests.

The programmer may decide to name the class with a more relevant name, e.g. ExamplesBooks and organize the code in the package called books. The programmer then must provide the name of this class (here books.ExamplesBooks) as the command-line argument to the main method in the class tester.Main.

Consult your IDEs for instructions on providing run-time arguments to the main method.

_________________________________________________________________________________

The programmer may define the tests in a several classes and name the methods with arbitrary names, providing the information about them through the use of Annotations. See the Annotations link for details.

If the programmer uses the relevant Annotations in any classes involved in the test run, the tester.Main class will first run all test cases defined though the use of Annotations and will then run the tests defined in the Examples class or in the class whose name has been provided as the run-time argument to the main method. (It may happen that some of the tests will be run twice.)

_________________________________________________________________________________

The programmer may define the tests in a class that implements the "IExamples" interface. This alternative lets the programmer determine exactly which test methods should be invoked for a particular test run. See the Select Tests: IExamples interface link for details.

Beginner’s option 2: implement the IExamples interface

The IExamples interface is defined as follows:

interface IExamples{

 

  // run all tests invoked within this method

  public void tests(Tester t);

}

Using the IExamples interface is a good way to centralize the selection of all tests that should be run (and enforce the order in which they will be performed). If the Example class implements this interface, it signals to the tester that all tests are invoked within an encapsulating tests(Tester t) method.

The purpose of this is to make altering your testing scheme quick and easy. Because all of the tests you wish to run are called or written within this one container method, it is simple to comment out a line and thus exclude a test. _________________________________________________________________________________

The interaction between IExamples and Annotations or test methods:

If the class that contains tests implements IExamples interface, the tester does not look for additional test methods. That maens that all other methods whose names start with test and consume an argument of the type Tester are ignored.

If you attempt to use the Java Annotations method of declaring tests, you must also be careful. If a class you have marked with @Example implements IExamples, it will ignore all @TestMethod annotations. You must add these to this tests method as well.

If you use the IExamples interface, and later decide to switch styles, you should remember to comment out or remove your tests method. This is because the tester will detect the tests method as a test, (because it starts with test and consumes an argument of the type Tester) and so will invoke all test methods within the tests method again. It is OK to leave the tests method in; just make sure that you are aware that the tests will be repeated.

_________________________________________________________________________________

The following example illustrates the use of the IExamples interface. Consider the following class:

import tester.*;

 

public class Book {

  String title;

  String author;

  int pages;

  boolean hardcover;

 

  public Book(){

    this.title = "No Book";

    this.author = "No One";

    this.pages = 0;

    this.hardcover = false;

  }

 

  public Book(String title, String author, int pages, boolean hardcover){

    this.title = title;

    this.author = author;

    this.pages = pages;

    this.hardcover = hardcover;

  }

 

  // compute the cost of this book

  public int cost(){

    return pages * (1 / 2);

  }

 

  // produce a book like this one, but with the given title

  public Book changeTitle(String title){

    return new Book(title, this.author, this.pages, this.hardcover);

  }

 

  // test the method 'changeTitle'

  public void testTitleChange(Tester t){

    t.checkExpect(this.changeTitle("New Title"),

    new Book("New Title", this.author, this.pages, this.hardcover));

  }

 

  // test the method 'cost'

  public void testCost(Tester t){

    t.checkExpect(this.cost(), this.pages * (1 / 2));

  }

}

Following the tester.Main variant, you would declare this class as the example class either by using the @Example annotation, or by passing "Book" as a run time argument to the compiler.

Then, the tester.Main class would run its main method, which would detect any @TestMethod annotations or any methods whose name begins with test. It would then execute these functions, and display a report.

Now let’s change our class definition slightly:

public class Book implements IExamples{ //...

and we add our tests method:

 

  // run all tests

  public void tests(Tester t) {

    testCost(t);

    testTitleChange(t);

  }

}

Now the tester will run this tests method. It is now much simpler to see which tests are being run, and alter which tests are processed. Simply comment out a test method invocation within the tests method, and it will be ignored. This makes it much easier to maintain your test code!

Before, you would have had to find and comment out an entire test method; in larger classes or classes that contain numerous tests, that can be an annoying process.

Using Annotations to define test classes and methods

Java allows the programmer to use Annotations to provide additional information about its classes, methods, and fields, that the program may use at the run time. The tester library uses Annotations to allow the programmer to specify that a class may contain test methods and to specify that a method with an arbitrary name is to be run as a test method. This methodology is much more powerful than the previous manner of invoking the tester library.

By adding the @Example above any class declaration, you can tell the tester to use that class as an example class (i.e. notifying the tester to look for the test methods defined in that class). This means that you can declare any number of classes to be example classes. The tester will run all of the tests contained within each class that is annotated as an @Example class.

The tester will still run every method whose name begins with test in every such class; however, this naming convention can be bothersome. Or, what if you already have a function that you would like to use as a test method, but it is referenced in numerous places in your code? In any class marked by the @Example annotation, the tester library will also run as a test every method marked with a @TestMethod annotation.

Note that methods annotated by @TestMethod must still take a Tester object as an argument.

_________________________________________________________________________________

Here is an example:

import tester.*;

@Example //declares this class to be an example class

public class Song {

  String title;

  String artist;

  int duration;

  int rating;

  boolean released;

 

  public Song(){

    this.title = "Nothing";

    this.artist = "No One";

    this.duration = 0;

    this.rating = 0;

    this.released = false;

  }

 

  public Song(String title, String artist, int dur, int rate, boolean released){

    this.title = title;

    this.artist = artist;

    this.duration = dur;

    this.rating = rate;

    this.released = released;

  }

 

  //change the title of this Song

  public void changeName(String title){

    this.title = title;

  }

 

  //calculate the cost of this song

  public int cost(){

    if(this.released){

      return 2 * this.rating;

    }

    return 0;

  }

 

  //will be run as a test

  @TestMethod

  public void doSomeStuff(Tester t){

    t.checkExpect(true, "hahaha");

  }

 

  //will also be run as a test

  public void testCost(Tester t){

    if(this.released){

      t.checkExpect(this.cost(), 2 * this.rating, "TestCost");

    } else{

      t.checkExpect(this.cost(), 0, "TestCost");

    }

  }

 

  //also run as a test

  public void tests(Tester abc) {

    testCost(abc);

  }

}

Note that making one or more of your classes serve as an example class does not invalidate those classes in any way. Being an example class merely indicates that the class contains test cases in addition to all of the material it is meant to contain as a part of your program.

Note that if you attempt to use the Java Annotations method of declaring tests, you must also be careful. If a class you have marked with @Example implements the IExamples interface (see Select Tests: IExamples interface), it will ignore all @TestMethod annotations.

Using Tester’s static methods to select reports

The Tester’s static methods: run, run, runReport, runReports, and runFullReport

The programmer can evaluate tests defined in any class by providing an instance of that class to one of the four variants of static run methods defined in the Tester class.

Typically, these methods should be invoked in the public static void main(String[] args) method of one of the classes in the project, following the typical standard Java practice. These methods eliminate the need set up special run configurations. This is especially valuable to those using the BlueJ IDE that requires the main method to be defined in one of the project’s classes.

The programmer still defines a class that contains all test methods and passes to the run method an instance of this class. This variant allows the programmer to define tests in several distinct classes and pass an instance of each of them to the run method for evaluation. The rules for the test definition and reporting of the results are the same regardless of how the test evaluation and reporting is invoked.

If you desire to have more localized, direct control over your testing, these are the methods for you. They are hooks into the same code in the Tester class that the tester.Main class eventually calls. The main differences are that these methods give you control over what kind of reporting you receive. Additionally, because these methods are called in your main function, it lends your testing toward a natural kind of organization.

You may need to use one of these functions depending on your IDE. If you or your IDE does not have the capacity to point your compiler to the tester.Main class’s main method, this might be your only solution. BlueJ is an instance of one such IDE that requires the use of one of these functions.