1

I'd like to write black-box unit tests for a method that calls another one with a delay. It kinda looks like this:

- (void) doSomething {
    // Do something
    [self performSelector:@selector(doSomethingLater) withObject:nil afterDelay:kDelay];
}

- (void) doSomethingLater { }

Where kDelay is a constant and doSomethingLater is private. The problem is that kDelay is 1 second and I don't want to slow down the execution of unit tests.

What would be the best way to unit test doSomething under a black-box approach (or as much as possible)?

The only thing that comes to mind is to add a method to the class to change the value of kDelay, but this could be used by accident by another developer. Is there a less invasive alternative?

hpique
  • 119,096
  • 131
  • 338
  • 476

3 Answers3

0

I don't know if it's less invasive, but you could swizzle the -[NSObject performSelector:withObject:afterDelay:] method with your own version that doesn't delay.

Swizzling means replacing a method implementation at runtime. Check out the free JRSwizzle library.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
0

The first thing I'd be looking at is detecting that the method was executed. So as @Rob said, I'd look into swizzling in a different method. Probably one local to the test that set a bool to indicate it had been called.

The next problem is reducing the delay. As KDelay is a constant you cannot really do anything about it. You could change it to a private property or something that you can access in a test and set, but is still private to the production code.

Another option (which I haven't looked into to see if it's possible!) is to not so much worry about detecting the method being executed, but look into whether you can detect that the call is the run loops stack, waiting to be executed. If you can detect the presence of the pending call rather than the call itself, then you can do this immediately and the delay becomes irrelevant.

drekka
  • 20,957
  • 14
  • 79
  • 135
0

Will your test want to make sure that doSomethingLater has been called and that it did what it was supposed to, and all you are trying to do is shorten the time delay? If so I recommend you create a private method that returns the constant value to doSomething. Something like:

@implementation MyClass

- (NSUInteger) getDelay {
    return kDelay;
} 

@end

Then you can stub out the implementation of getDelay at runtime using various features of things like OCMock other test frameworks.

Tim Dean
  • 8,253
  • 2
  • 32
  • 59