+[NSDate date]: unrecognized selector sent to class – Why you shouldn’t stub foundation classes

About a month ago I was banging my head against a wall when our Jenkins build box would intermittently crash the unit test job with output like below:

Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '+[NSDate date]: unrecognized selector sent to class 0x104369f58'
terminating with uncaught exception of type NSException
abort() called

This just didn’t make sense to me at first.  How can there not be a +date method on NSDate thats crazy? So I ran our unit tests via our rake task on my machine 20 times in a row without fail (this is the exact same task that our mac mini jenkins slave is using).

After digging a littler further and actually reading the crash logs it highlighted that the crash wasn’t originating from the main thread at all but another thread that seems to be used solely by XCTest to generate the logging output.

Thread 10 Crashed:: Dispatch queue: com.apple.XCTest.infrastructure-logging
0 libsystem_kernel.dylib 0x000000010495d282 __pthread_kill + 10
1 libsystem_sim_c.dylib 0x00000001046ed23a __abort + 145
2 libsystem_sim_c.dylib 0x00000001046ed1a9 abort + 144
3 libc++abi.dylib 0x00000001044b6481 abort_message + 257
4 libc++abi.dylib 0x00000001044de3d5 default_terminate_handler() + 267
5 libobjc.A.dylib 0x0000000103d5be19 _objc_terminate() + 103
6 libc++abi.dylib 0x00000001044dbb01 std::__terminate(void (*)()) + 8
7 libc++abi.dylib 0x00000001044dbb73 std::terminate() + 51
8 libobjc.A.dylib 0x0000000103d5bd93 objc_terminate + 9
9 libdispatch.dylib 0x000000010460eb08 _dispatch_client_callout + 28
10 libdispatch.dylib 0x00000001045f98cf _dispatch_queue_drain + 733
11 libdispatch.dylib 0x00000001045f9494 _dispatch_queue_invoke + 217
12 libdispatch.dylib 0x00000001045fb3fa _dispatch_root_queue_drain + 479
13 libdispatch.dylib 0x00000001045fc2c9 _dispatch_worker_thread3 + 98
14 libsystem_pthread.dylib 0x000000010498e6cb _pthread_wqthread + 729
15 libsystem_pthread.dylib 0x000000010498c4a1 start_wqthread + 13

What are our unit tests possibly doing?

For this project it is important to inform you that we are using the excellent testing framework Kiwi for our unit test suite.  Our application like most do have some logic geared around today compared to another day.  From memory we are determining how old in years a person is, which requires the use of todays date in order to make the calculation.

With the information in hand we can narrow down looking at any tests that are messing with [NSDate date].  Sure enough we had tests (I wrote some of them :) ) that were using kiwi to stub NSDate like so.

[NSDate stub:@selector(date) andReturn:someFixedDate];

This looks harmless enough right? I am just using a method Kiwi provides which makes it really easy to stub a method call. However once we dig into the Kiwi source code and see how it performs stubbing you know the only way it is possibly doing this is via method swizzling. And as we know method swizzling is a way to swap one method implementation out for another using the ObjC runtime with a simple call to class_replaceMethod.

Warning to all, don’t mess around with Foundation classes, even in testing

It has been written before on NSHipster, and I even remember reading the article and going yeah makes sense you don’t want to mess around with Foundation classes who knows what could happen.  Little did I appreciate what was actually going on under the hood when I made the innocent calls to stub date on NSDate.

How we resolved it

In the end the fix we did was quite easy, we simply created a new class XXXDate and created a new method for simplicity called now which simply proxies the call to [NSDate date]. So everywhere in our app we know when we want to use todays date we use [XXXDate now], and

 

Leave a Reply