Testing Inside Framework

Situation

While working on a framework I encountered the problem of not wanting to expose certain header files solely for testing purposes. To me it seemed just wrong to add these “internal” headers to the public interface so I can write the code following the TDD paradigm.

The framework needs to be as it is going to be delivered, only those interfaces that I want to expose need to be tagged as being public in your build settings.

What to do?

Solution

Today I found the solution to this problem. In the end it turned out to be quite simple.

In your build settings of your test target, just add the following to the User Header Search Paths: $(PROJECT_DIR)/<Folder name of your project> and make sure that the setting is recursive.

Presto now you can simply add the <classname>-Internal.h import line to your test class.  And like magic you can create your unit tests where you can access your internal properties and do some validation on your implementation.

Bonus:

And when you have a mix of Swift and Objective-C code in your framework you will encounter a similar problem. When using Swift code in your frame work you need to use the “Objective-C Generated Interface Header Name’, in most case this is in the format: “$(SWIFT_MODULE_NAME)-Swift.h” Where your SWIFT_MODULE_NAME is your project name. So there is a header file you can use inside your Framework code. However you can, and would not want to, expose this generated header in your framework. You only need it inside, right? So the same rhythm applies. We need to add this generated header to your search path of your test target. Now the problem is, where is that darn file. It turns out it is in the Derived Data folder.

Using the “trick” of outputting all build settings

“xcodebuild -project <Projectname>.xcodeproj -target <target> -showBuildSettings > BuildSettings.txt”

I found a nice parameter: OBJROOT, this parameter pointed to my local path of the derived data, let’s further investigate, opened the folder in the finder and since only run unit test on the Mac to look in the Debug-iphonesimulator folder.

So for me the path turned out to be:

$(OBJROOT)/<Projectname>.build/Debug-iphonesimulator/<Projectname>.build/Objects-normal/x86_64/

I added this path also to the search path as I did before, and now I can use the Objective-C Generated Interface Header Name as an import, same as you would do in your implementation.

 

disclamer:

For me this works, and it works on my Jenkins run CI/CD server. However no guarantees what so ever… 😉