Unit Tests

Introduction

Test driven development is an important methodology adopted in professional software development. Floodlight adopts the Junit framework and EasyMock for unit testing.  A very helpful while easy-to-follow first read about this testing methodology can be read at the IBM article "Easier testing with Easy Mock".

You can run all the Junit tests and check the unit test coverage just by using ant, the Java ant command:

# runs the unit tests with coverage
ant coverage

browse unit test reports at floodlight/target/coverage/index.html

Whenever you develop new code you are required to add unit tests to cover the majority of your code and range of test cases.  And, if your code touches any existing classes even without changing them, consider extending the unit tests for that class to improve floodlight's overall test coverage. Again, this is really a community effort. 

Example

You should have a pretty good understanding already after the first reading above. Just in case you need a bit more intuitive help to get started, consider reading the existing unit tests in the Floodlight source tree under src/test/java. You could perhaps start with net.floodlightcontroller.forwarding/ForwardingTest.java or net.floodlightcontroller.devicemanager.internal/DeviceManagerImplTest.java.

Intuitively, one possible process to start writing an EasyMock test is:

1. Have a clear picture of what you will be "mocking" and what you will be testing: 

Remember your purpose is to test your code AND ONLY your code.  As your code executes, inevitably it will call other classes' methods.  Those are the classes that will be "mocked".  In ForwardingTest.java, for example, you will see that many "mocked classes" that are directly being instantiated.  These classes can be found in the src/test/java/*.test packages.  These are convenient classes to use when you need to mock the typical Floodlight services that your module will most probably depend on.

At the same time, in the example you will see a number of createMock(*.class) calls.  These are the basic forms of mocked class instances.  If you are not sure which method is best to create the mock for a class, just look around the existing tests and the Mock* classes for inspiration.

2. Track down all methods of the mocked classes that your code will be calling.

Any method used from a mocked interface must be "expected" with an expect statement, with the input argument values and desired return values explicitly assigned.  You are basically going through a "virtual execution" of your code in your mind, and note down when you will call what methods, and what values you plan to feed in your designed test scenario, and what is the correct value that should be returned.  These values are typically chosen to cover the normal and boundary cases, and you can design multiple separate unit tests, each of which will test a certain set of values.

3. createMock(), reset(), expect(), replay(), verify()

In general, the process of a test involves first creating all the mock class objects using createMock(), then reseting the object with reset(), then specifying all the expect statements you noted down earlier, following the expect statements is replay() which put things in "ready" state, now you finally will run your developed code that you want to test. After this, apply verify() to all the mock objects to check whether the methods you expect to be called are all called and with the values you expected.  If anything doesn't match, you either have a bug or the unit test was not correctly set up.  

4. Test coverage

'ant coverage' analyzes the portion of code that is executed by your unit tests.  Such a measurement, even if 100%, does not necessarily == you have a perfectly correct code.  The correctness depends on the range of different test cases that you designed, using normal and boundary values to stimulate all the potential behaviors of the code. In practice, you will have many @Test cases in your *Test.java for your module.

Additional Reading about Junit and EasyMock

Junit Tutorial (1)
Junit Tutorial (2)
Unit testing with JUnit and EasyMock
Mock controls with EasyMock
Using captures with EasyMock
EasyMock README

Known caveats and oddities:

  • assertTrue has two signatures, one with a string message and one without. If you use the one without and the assert fails, you will get "Assertion Failed: null". The null just means that the string message is null it doesn't mean that there was a Null Pointer Exception in your expression. So, to avoid confusion, please either use a message whenever you use assertTrue, or use assertEquals(true, expression)