I learned something very useful today. My group uses Apple’s XCTest as a framework for test automation. Because of the way XCTest gets associated with the app, there are certain rules you need to follow that aren’t completely obvious in order to use certain very common features of CocoaTouch.
How XCTest Can Test Your App
When you create a test target to test your app, a separate executable image is built that doesn’t include the components of your app. When you run a test, your app is run and then the test image is injected into the app process. The result is that type information comparisons (e.g. isKindOfClass:) only work if the class objects are in the same image.
Fixing Type Comparisons
In order to compare types reliably, you must not include the same source files in both the app target and the test target. If your project is small, this is a relatively simple task. However, as your project gets larger and you start using CocoaPods or include libraries in other ways, you need to get a little more creative.
In our case, we use CocoaPods pretty extensively, and we build some of our code in libraries (and therefore separate pods) and some in the application. The result is that I needed to create multiple pod targets to ensure that no source file was included in both targets.
Other XCTest Tidbits
I ran into this problem because I was building some tests with mocks and other tests without them, and the mocks were located in a separate pod. In order to get my app to use the mocks, I created an app target that included the mocks directly. Why, you might ask? For the following reasons:
- My app view controller need access to the data that is being mocked before my tests are run.
- Setting a singleton value in a +load method doesn’t work because the +load method in the test target isn’t called
- Swizzling in the test target doesn’t work.