Unit testing Objective-J apps with OJUnit

For a new framework/language to rapidly be adopted is has to provide an easy way for developers to add and run tests. Not only unit tests are the cornerstone of maintainable code, but they can also help you learn a new language by writing tests to figure out behaviors.

OJTest is the latest test framework from 280North. It contains OJUnit - an xUnit framework and OJMoq - a mocking framework as well a couple of tools (OJAutotest - a tool to automatically test your Cappuccino application when you make changes and OJCov - a coverage tool to help you identify code not covered by tests).

We're going to focus on OJUnit first. OJUnit is a simple unit test tool written in and for Objective-J. It's based on the xUnit design. If you used JUnit, you should feel right at home.

Installing

Assuming that you have Cappuccino 0.8 or later installed, you should be able to install OJUnit with the following command:

$ tusk install ojunit

Writing tests

Like any other xUnit framework, test classes must respect a few conventions:

  • Test class names should follow the pattern ClassNameTest.
  • Test classes must subclass OJTestCase.
  • Test case methods name must start with test.
  • Tests should be placed in a Test folder at the root level of your project.

Here is a skeleton test for testing SomeClass.j class

@implementation SomeClassTest : OJTestCase
{
}
- (void)setUp
{
   // Invoked before any tests are run
}
- (void)tearDown
{
   // Invoked after all tests are run
}
- (void)testAlwaysTrue
{
    [self assertTrue:YES];
}
@end

Here is a list of assertions methods available:

- (void)assertTrue:(BOOL)condition
- (void)assertTrue:(BOOL)condition message:(CPString)message
- (void)assertFalse:(BOOL)condition
- (void)assertFalse:(BOOL)condition message:(CPString)message
- (void)assert:(id)expected equals:(id)actual
- (void)assert:(id)expected equals:(id)actual message:(CPString)message
- (void)assert:(id)expected notEqual:(id)actual
- (void)assert:(id)expected notEqual:(id)actual message:(CPString)message
- (void)assert:(id)expected same:(id)actual
- (void)assert:(id)expected same:(id)actual message:(CPString)message
- (void)assert:(id)expected notSame:(id)actual
- (void)assert:(id)expected notSame:(id)actual message:(CPString)message
- (void)assertNull:(id)object
- (void)assertNull:(id)object message:(CPString)message
- (void)assertNotNull:(id)object
- (void)assertNotNull:(id)object message:(CPString)message
- (void)assertNoThrow:(Function)zeroArgClosure
- (void)assertThrows:(Function)zeroArgClosure
- (void)assert:(CPString)aRegex matches:(CPString)aString

And here are the methods to fail a test:

- (void)fail
- (void)fail:(CPString)message
- (void)failSame:(id)expected actual:(id)actual message:(CPString)message
- (void)failNotSame:(id)expected actual:(id)actual message:(CPString)message
- (void)failEqual:(id)expected actual:(id)actual message:(CPString)message
- (void)failNotEqual:(id)expected actual:(id)actual message:(CPString)message

For more details, look at the source code for the OJTestCase class.

Running tests

OJUnit tests can be run using the ojtest command. At the top-level directory of your project simply run

$ ojtest SomeClassTest.j

with the filenames of all your tests. It is important that you are at your project's top-level directory, or Objective-J may not be able to locate your files correctly.

Alternatively, you can run all your tests using wildcards, like:

$ ojtest Test/*Test.j

This will run all the tests with filenames that end in Test.j located in the directory Test/.

To simplify test execution, you can add a task to your jakefile

task("test", function()
{
    var tests = new FileList('Test/*Test.j');
    var cmd = ["ojtest"].concat(tests.items());
    var cmdString = cmd.map(OS.enquote).join(" ");

    var code = OS.system(cmdString);
    if (code !== 0)
        OS.exit(code);
});

and then invoke it with

$ jake test

Happy testing!

Talk Back