I am in the process of migrating my project from Xcode 4.6.3 to Xcode 5.0.2. The project's unit tests were developed with SenTestingKit/OCUnit. Now when I am running the tests in Xcode 5, I get an error from the RunUnitTests
script telling me that
RunUnitTests is obsolete.
Possibly related is this note in the Xcode 5 release notes:
SenTestingKit and OCUnit are deprecated. Use the migrator to move to XCTest.
Unfortunately, I have not been able to find out more about this mysterious "migrator". Possibly my google-fu is lacking [again], so my main question is: How do I migrate unit tests from SenTestingKit/OCUnit to the new XCTest (with or without the "migrator")?
A secondary question, in case migrating is a complicated business: Is it possible to get Xcode 5 to run unit tests that are still based on SenTestingKit/OCUnit? After all these are merely deprecated, so they should still be around and functional.
Thanks to Shaggy Frog's answer we know that the mysterious "migrator" mentioned in the Xcode release notes is a wizard launched by selecting "Edit > Refactor > Convert To XCTest". I am going to write about my experience with this wizard in two parts. The first part is an incomplete answer to the primary question, the second part answers the secondary question.
The first thing you need to realize is that for the wizard to work, you need to select a unit test target. If you have the main target selected, the wizard simply does not list any targets to convert.
Once I found out about this, I was able to step through the wizard, but in my case the end result was still a spectacular failure! The wizard claimed that no source changes were necessary and that only build settings needed to be updated to migrate to XCTest. In the end, the wizard did not even manage to do that correctly: It did remove the reference to the SenTestingKit framework, but it did not put in a reference to the XCTest framework.
Anyway, what follows is a list of the changes that I had to manually make because the wizard failed to make them for me. If the wizard works better for you, you may not need to do all of these things.
SenTestCase
to XCTestCase
<SenTestingKit/SenTestingKit.h>
to <XCTest/XCTest.h>
octest
to xctest
.ST*
to XCT*
(e.g. STAssertTrue
becomes XCTAssertTrue
)STAssertEquals
needs to be renamed to XCTAssertEqual
(note the missing "s" at the end). You will know that you have forgotten about this if you get this compiler warning: warning: implicit declaration of function 'XCTAssertEquals' is invalid in C99
nil
to be passed as the failure description. For instance, XCTAssertNotNil(anObject, nil)
is not possible and must be changed to XCTAssertNotNil(anObject)
. You will know that you have this problem when you get this compiler error: error: called object type 'NSString *' is not a function or function pointer
.NSString
class method stringWithFormat:
does. You will know that you have this problem when you get this compiler error: error: expected ')'
. Some examples:NSString* formatSpecifier = @"%@";
NSString* failureDescription = @"foo";
// These are OK
XCTAssertNotNil(anObject, @"foo")
XCTAssertNotNil(anObject, @"%@", failureDescription)
// These are not OK
XCTAssertNotNil(anObject, failureDescription);
XCTAssertNotNil(anObject, formatSpecifier, failureDescription);
Last but not least, as already mentioned further up, the reference to the XCTest framework needs to be added to the unit test target. You will know that you have forgotten this if you get linker errors such as Undefined symbols for architecture i386: "_OBJC_CLASS_$_XCTestCase", referenced from: foo
.
Xcode 6 update: Linking against XCTest is no longer required in Xcode 6 (in fact XCTest is not even listed as an available framework anymore). Instead set the build setting CLANG_ENABLE_MODULES to YES (exposed in the UI as "Enable Modules (C and Objective-C)"). This will cause clang
to automatically link against XCTest when it sees an #import <XCTest/XCTest.h>
statement. Details are available in the "Modules" section of the clang documentation.
At this point I got a linker error that made me realize that my mission to migrate to XCTest had failed. The reason: XCTest is not part of SDK 6.1, but I am still building my project with base SDK iOS 6.1 (this SO answer explains how to integrate SDK 6.1 into Xcode 5).
Since I am unable to continue with the migration, my solution for the moment is therefore to keep my unit tests based on SenTestingKit/OCUnit, until I find the time to upgrade my app to iOS 7. This is what I had to do in order to get the unit tests to run:
The final solution is not as good as in Xcode 4.x where the unit tests were executed automatically every time that I ran the main target's "Run" or "Build" action. Unfortunately, it seems that I can't get this to work without a "Run Script" build phase.