May 23rd, 2012
Filed under: Mac Development | 3 comments
In a previous post I covered how to setup OCUnit unit testing in Xcode 4. This post provides an introduction to writing unit tests with OCUnit, which is a unit testing framework for Objective-C code that comes with Xcode.
When writing unit tests with OCUnit, you will create one or more unit testing classes. How many classes you create is a matter of personal preference. I usually have one unit testing class for each class in my application. If you have a large application class, you could create multiple unit testing classes for that class. You could also have one class that contains all your unit tests.
An OCUnit unit testing class contains two files: a header file with a .h extension and an implementation file with a .m extension. A newly created class’s header file looks similar to the following:
@interface TrieUnitTest : SenTestCase @end
The unit testing class, TrieUnitTest in my example, inherits from the SenTestCase class, which is OCUnit’s base class. If you want to add any data members to the unit testing class, add them between the @interface and @end tags. A common data member to add to a unit testing class is an object of the application class you’re testing.
You generally won’t do much in the header file. In an Objective-C application you must declare a class’s methods in the header file.
- (void)DoSomething;
You do not need to declare your unit testing methods in the header file. Write your unit testing methods in the implementation file.
What appears in a newly created class’s implementation file depends on the version of Xcode you’re using and how the class was created. If you add a new unit testing class to a project in Xcode 4.3.2, you get an empty implementation to fill in.
@implementation TrieUnitTest @end
If you create a new Xcode project in Xcode 4.3.2 with unit testing support, the unit testing class Xcode supplies has three methods: setUp:, tearDown:, and testExample, which creates a failing test. I will cover the setUp: and tearDown: methods later in this article. You can delete the testExample method, or you can keep it initially to see if unit testing is working.
An OCUnit test method has the following requirements:
The following code shows an example of defining a test method:
- (void)testDoSomething { }
You do not need to define test methods in the header file. Write the method in the implementation file.
One thing to keep in mind when writing test methods is you have no control over the order in which OCUnit runs your tests. Each unit test you write should be self-contained and should not rely on the results of other tests.
Assertion macros are the heart of unit testing. They determine whether or not the test passes. OCUnit has the following assertion macros:
In most cases a unit test should have only one assertion. One assertion per test makes it easier to find where a failing test fails.
Now it’s time to write a unit test. I’m going to test that multiplying two negative integers yields a positive integer. Here’s the test method:
- (void)testMultiplyingTwoNegativeIntegersYieldsPositiveProduct { int x = -5; int y = -7; int product = x * y; STAssertTrue((product > 0), @"The product was less than zero."); }
The most interesting part of the test is the assertion. I use the STAssertTrue macro because I’m testing a condition, whether the product is positive. I pass two arguments to the STAssertTrue macro. The first argument is the condition to test. The second argument is the error message OCUnit displays if the test fails. The @ before the first quote specifies that the error message is an Objective-C string.
If you add this test to one of your unit testing classes, it should pass. If you want to see what happens when a test fails, make x or y positive, change the condition in the assertion, or change STAssertTrue to STAssertFalse.
The test I wrote in the last section shouldn’t fail, but if it does, the error message doesn’t help much. The error message tells you the product was less than zero, but it doesn’t tell you what the product’s value was, which is important to know. Let’s improve the error message.
STAssertTrue((product > 0), @"The product was less than zero. Product: %d", product);
Now if the test fails, it prints the value of the product variable. The %d in the error message is a format string that specifies printing a variable’s value as a decimal integer. If you have printed variables using NSLog or printf, you’re familiar with format strings. Use %f to print a floating-point variable. Use %s to print a string of characters.
You can print multiple variables by using multiple format strings and supplying the extra variables as arguments to the assertion. The following assertion tests that x and y are equal and prints their values if the test fails:
STAssertEquals(x, y, @"x and y were not equal. x: %d y: %d", x, y);
Two optional methods that can assist you in unit testing are setUp: and tearDown:. If you have initialization code to perform before running multiple tests, you can place that code in the setUp: method to remove duplicate code in your tests. The tearDown: method contains cleanup code. A common case of using the tearDown: method is to free any memory you allocated in the setUp: method.
OCUnit calls the setUp: method before running each test and calls the tearDown: method after running each test. If your unit testing class has 20 tests, OCUnit calls setUp: and tearDown: 20 times.
The following example shows a minimal setUp: method:
- (void)setUp { [super setUp]; // Set-up code here. }
Notice how you call the parent class’s setUp: method before writing your setup code.
The following example shows a minimal tearDown: method:
- (void)tearDown // Tear-down code here. [super tearDown]; }
Notice how you call the parent class’s tearDown: method after writing your cleanup code.
Tags: unit testing
[…] […]
Its really helpful to learn how to write test cases.
But I want to know the deference between Logical unit testcases and application unit testcases.
how do i write application unit test cases?
could you please more elaborate with an example?
Brinda,
Xcode no longer distinguishes between logic and application unit tests so you don’t have to worry about the distinction. If you add a test case class to a project in Xcode 4.6, you’ll notice the only thing Xcode asks for is a class prefix. You can learn more in the following article:
OCUnit Application vs. Logic Tests
If you still want to know about the difference between logic and application test cases, the following Google search provided some explanations:
xcode application vs logic unit tests