Unit-Tests in Objective-C

Flying Kiwis

Jan Olbrich
Mobile Quality
4 min readAug 17, 2017

--

Every language has their own rules. It does not matter if you look at the syntax, language features or type of language. Everything changes and at the same time it stays the same. In our themed app we will be using swift, but since Objective-C is the origin of iOS development, we make a little detour, to see how specifics are done here.

I'm not a big fan of XCTest. Don't get me wrong, it does it's job just fine.. but that's it.. just fine. Understanding the whole state of the app and action we are trying to test, depends on our ability to name tests. In general we suck doing so. An alternative are frameworks based on RSpec. They add syntactic sugar to our tests and might just make the difference of searching 2 hours understanding the test, or fixing the problem.

Kiwi

For Objective-C this is Kiwi. It offers different stages in which we can describe, what we are testing and in which state it is in:

  • describe: Describes the system under test (sut)
  • context: Describes the state of the sut

Regarding these definitions we can write a simple testfile structure with a demo class:

Hacking private

I've already mentioned every language has their own quirks. In Objective-C private does not exist. Instead it is only visibility for the compiler. We can still send a message via the runtime to a specific instance of an object and thus ignore the visibility.

Okay.. we are not going to start using objc_msgSend. Don't worry! Instead we can use categories to do this job. In our testclass we will add a category containing all the private properties and methods we want to test, which are not accessible from the outside. Doing so will provide visibility to the compiler and we can just send any messages to objects, even though they are considered private.

Stubbing

Every now and then we have to rely on API’s we don’t have control over. This can be simple SDK API’s or network services.
Imagine having a login view controller. How can you test this? Especially when your tests are supposed to run in a dark cave with no external dependencies whatsoever?
Welcome to mocking and stubbing. In this post we’ll take care of how to stub method calls in unit and integration tests. The idea of stubbing is to define the behavior of a method call either by specifying the return value, or by simply replacing it.To stub a method we need the object responding to this method. Having this, we can simply define the return value by writing:

[testClass stub:@selector(method) andReturn:Value];

The value can be anything: boolean, NSString, NSInteger or whatever you return. Kiwi doesn’t care about that. With this we can simply replace the login method of our local network server.
Simple isn’t it? But what do do we, when we can’t get ahold of the instance of the used class?

Hardcore Stubbing

For this there is method swizzling. This is a part of black magic within Objective-C and shouldn’t be used carelessly. But if you know what you are doing, and you use it for testing, you might be able to get some value from it.

So what is method swizzling? Basically during runtime you add another method into a class, and switch the signature of this method with another. Regarding our prior described login method, let’s add another method called swizzleLogin. By swizzling the login method with swizzleLogin every call to login will now execute swizzleLogin and the other way around.
This can easily be done wrong, so let’s introduce JRSwizzle. This will swizzle your methods correctly.

So how do I use this?

1. create a category
2. override load
3. create swizzle method

So let’s see how the result would look like. I have a class called DemoModel and I want to swizzle its text method. Since I want to make sure it is only executed once (imagine swizzling twice.. we get the original result) I wrap it all in a dispatch_once call.

That’s it. Whenever the text method in DemoStore is called it is now redirected to swizzleName. In case you want to still call the original text method after swizzleName is executed, just call swizzleName within the original swizzleName method.
Yes there should be alarm bells ringing in your head, but since we redirected, it will in this case, just call the original text method.

Another interesting part is that it is enough to add this category to your target. You don’t have to include it anywhere. So be careful, and make sure it is not included in your main target.

Conclusion

As you can see there are quite some specifics to know, when testing in Objective-C. Knowing these will help you in improving your tests. This post is by all means not exhausting all possibilities of testing, stubbing and mocking. There are other nice frameworks we might look at, at a later time (e.g. OCMock)

Next: Unit-Tests in Swift

Previous: Test Driven Development

--

--

Jan Olbrich
Mobile Quality

iOS developer focused on quality and continuous delivery