Creating a Unit Test

This tutorial briefly describes how to create a unit test for your new module.

Python Nose

RLPy uses nose to perform unit tests. The syntax is:

nosetests <directory or file to test>

If a directory is supplied, nose attempts to recursively locate all files that it thinks contain tests. Include the word test in your filename and nose will search the file for any methods that look like tests; ie, again, include test in your method name and nose will execute it. Note for example, the tabular domain can be tested by running:

nosetests rlpy/tests/test_representations/test_Tabular.py

And that all representations (that have tests defined) can be tested by running:

nosetests rlpy/tests/test_representations/

And that in fact all modules with tests can be tested by running:

nosetests rlpy/tests/

Warning

The last command may take several minutes to run.

Unit Test Guidelines

There are no technical requirements for the unit tests; they should ensure correct behavior, which is unique to each class.

In general, each method should start with test and contain a series of assert statements that verify correct behavior.

Test:
  • preconditions
  • postconditions
  • consistency when using a seeded random number generator
  • etc.

Pay special attention to corner cases, such as when a Representation has not yet received any data or when an episode is reset.

Example: Tabular

Open rlpy/tests/test_representations/test_Tabular.py Observe the filename includes the word test, as does each method name.

Many, many tests are possible, but the author identified the most pertinent ones as:

* Ensure appropriate number of cells are created
* Ensure the correct binary feature is activated for a particular state
* Ensure correct discretization in continuous state spaces

These have each been given a test as shown in the file.

The integrity of the module is always tested with a statement of the form assert <module quantity> == <expected/known quantity>.

For example, the code:

mapname=os.path.join(mapDir, "4x5.txt") # expect 4*5 = 20 states
domain = GridWorld(mapname=mapname)
rep = Tabular(domain, discretization=100)
assert rep.features_num == 20
rep = Tabular(domain, discretization=5)
assert rep.features_num == 20

creates a 4x5 GridWorld and ensures that Tabular creates the correct number of features (20), and additionally tests that this holds true even when bogus discretization parameters are passed in.

What to do next?

In this unit testing tutorial, we have seen how to

  • Write a successful unit test
  • Use nosetests to run the tests

You should write unit tests for any new modules you create. Feel free to modify / extend existing unit tests as well - there is always more to test!