Writing Unit tests
Any method and function call you write is eligible for a unit test. Consider writing a
separate test for every unique behavior exhibited by your method/function.
For example, if you are testing setName: , and you've decided that
nil is unacceptable, while any instance of NSString is fine, then
write a testNullName method, which makes sure that setName:
behaves one way when nil is passed in, and another way when a valid
NSString is passed in.
Two common methods of grouping unit tests are by class and by state. Grouping tests by class
makes it easy and convenient to find and create new tests. Grouping tests by state enables
you to use a single Fixture to set up an object graph, and
arrange your unit tests accordingly. Most test suites use a combination of methods depending
on their needs.
Use TWMockObject to help you concentrate on testing new code, and avoid duplicating exising
tests. For example, if you're testing an auditing system, set up a fixture so that the
accounts you are auditing are instances of TWMock. The goal is to concentrate your testing
to the specific behavior of your Auditor , and avoid retesting
Account.
Running Unit Tests
TestKit gives you the tools you need to run your tests automatically at each build, manually
from TestRunner, or scripted ( or manually ) from
TestRunnerHelper. If you want tests to run at each
build, create your target by selecting the TestKit/TestBundle target.
While running all your tests during every build cycle is a great way to insure the
integrity of the build, there are times when it is more productive to concentrate on a
particular subset of your tests. To limit the tests that get run during the build, modify
the 'Shell Script Files' of your build phase by specifying the fixture you are
interested in:
export DYLD_FRAMEWORK_PATH=$PWD/build
./build/TestRunnerHelper -pb "./build/$PRODUCT_NAME.$WRAPPER_EXTENSION:MyTestClass"
You can specify certain test cases if you wish:
export DYLD_FRAMEWORK_PATH=$PWD/build
./build/TestRunnerHelper -pb "./build/$PRODUCT_NAME.$WRAPPER_EXTENSION:MyTestClass.testOne"
You can also specify multiple tests:
export DYLD_FRAMEWORK_PATH=$PWD/build
./build/TestRunnerHelper -pb "./build/$PRODUCT_NAME.$WRAPPER_EXTENSION:MyTestClass" \
"./build/$PRODUCT_NAME.$WRAPPER_EXTENSION:MyOtherTestClass.testTwo"
Be sure to remove your specific test cases when you are finished modifying/debugging your
code.
Debugging unit tests
If you prefer to use logging statements to debug your code,
TestRunner conveniently separates stdout
and stderr . You can use your own logging techniques, or add the line
#import <TestKit/TWLoggingMacros.h>
to your code, and use the macros defined inside. TestRunner creates a new Objective-C
runtime and reloads your bundle every time you run your tests, so there is no need to
restart TestRunner between builds. Deselecting passing tests is a convenient way to make sure
that TestRunner is only executing the tests that need debugging.
If the debugger is more your style, you'll have to do a little set up for each
project you wish to debug:
- Open the XCode project with targets you wish to debug
- Select Project->New Custom Executable
- Press 'Choose...' in the assistant dialog box
- Choose '/Applications/TestRunner/TestRunnerHelper'
- Fill in an executable name of your choosing
- Select the project you want to add it too
- Select 'Next'
- In the resulting editor, add the names of the test bundles you wish to debug to the
'launch arguments' list (myTests.bundle)
In order to debug a test bundle:
- Make sure the bundle you intend to debug is the current target. This insures that
your source code is rebuilt if necessary
- Set breakpoints as you normally would
- Select your custom executable in the 'Groups & Files' view
(Executables->TestRunnerHelper)
- In the 'Details' view (top right pane), make sure the radio button next to the
TestRunnerHelper is selected. This ensures that TestRunnerHelper will be the main
executable run when you select 'Debug->Run Executable'
- In the editor pane, make sure the launch argument representing your target bundle is
selected, and that no other launch arguments are selected. This step ensures that
your test bundle is passed to TestRunnerHelper on the command line
You can be more specific in your launch arguments to save execution time:
myTests.bundle:MyTestClass.testOne specifies a specific test to debug
|