Asynchronous unit tests using XCTestExpectation

I came across an interesting blog post by Phil Beauvoir about Asynchronous Unit Testing in Xcode 6 using the new XCTestExpectation. It explains how he converted existing Objective-C asynchronous iOS unit tests to Swift, at the same time making them more efficient and readable by using an instance of XCTestExpectation and waitForExpectationsWithTimeout().

XCTestExpectation is a dirt simple class with 1 method: fulfill(). Create an instance of it and call fulfill() when the expectation has been met.

In your test assert section, call waitForExpectationsWithTimeout(), passing the XCTestExpectation, and Voila! Asynchronous unit tests.

As opposed to using a spin loop, this explicitly informs the XCTest framework that your test is waiting for an asynchronous event to happen, allowing it to perform other tests while waiting.

Testing Storyboards in Swift?

Awhile back I blogged about how to test storyboards by loading the ViewController from a Storyboard. Recently I’ve been trying to figure out how to do the same thing in Swift.

After fumbling around for awhile with the Swift test case file, and not making much progress, I decided to try something different, and tried adding an Objective-C test case file to my Swift project. At least in this case, I know how to load the ViewController from the Storyboard. And this actually worked.

This turned out to be fairly simple. Let me warn you though, that I consider this approach to be a temporary hack, albeit an interesting one. I believe the correct solution will be to figure out how to do this in a Swift unit test case file. At this point though, I’m not sure if I’m being prevented from doing so due to bugs in beta level Xcode 6, or just my lack of Swift knowledge.

But caveat emptor aside, here is how to create an Objective-C unit test case file in an otherwise entirely Swift project, and use it to load and test a ViewController from a storyboard:

  • Set the product module name so that you will know the swift header file name to import (eg. “#import ProductModuleName-swift.h”). Note that this should not be necessary, as the Product Module Name is supposed to default to the Product Name. This wasn’t working as of Beta 2.
  • Add an Objective-C unit test case file. Make sure to set the language to Objective-C. It will default to Swift.
  • I did not have to create an Objective-C Bridging  Header for the Swift ViewController, because I’m only going the other way, from Swift to Objective-C, but I did anyways when prompted. I don’t think having it will hurt anything, and you might want it later.
  • Import the swift header file into the Objective-C unit test case file:
    #import “ProductModuleName-swift.h”

That was it. This then gave me access to my ViewController definitions. Then use the previous described method of loading a storyboard and viewcontroller from it.

I will update this article once I figure out how to do it correctly in a Swift unit test case file.

Unit testing in Swift

The iOS folks around here are all abuzz about the new Swift language announced by Apple this week. Surprisingly, this information was released publicly, so no NDA required to talk about it.  You can find the information at https://developer.apple.com/swift.

While there are still a lot of questions about the implications of testing in Swift, at first glance it looks pretty straight forward. One nice surprise, reported yesterday on BendyWorks.com, is that  Setup() isn’t needed to create fresh instances of the object-under-test. Simply creating a as a constant stored property of the test case class results in a fresh instance being created for each test.

I’ve run through some TDD katas using Swift, and after some initial adjustment to the new language, I have to say that things appear to work fairly well. I’m really warming up to Swift.

Now the next question is whether or not the existing mock frameworks still work, or how soon we can get updates. No comment from Jon Reid yet, but I’ll bet he will be speaking out soon :-)

Testing Singletons

A topic that keeps coming up in iOS development is the use of Singletons. I had thought that I had written about these awhile back, but now realize that I covered these in the Testing Singletons chapter of the Unit Testing iOS Applications with Xcode 4 lynda.com course, but not written about them here. The mention of Xcode 4 probably keeps folks from watching this video, now that Xcode 5 is out. So I’m going to repeat some of what was said, but encourage you to watch the video anyways. Much of what is covered in that course is still very relevant.

Unrestrained, and used without discipline, Singletons can be evil. Apple uses them a lot, so it’s easy to see why developers would think that it is perfectly ok to use them a lot also. I’m not going to get into the dangers of using Singletons, other than to point out that they are essentially just global variables. There are plenty of resources available describing the reasons to be wary of using global variables, so I won’t belabor the point here.

What I will address here is the difficulty of testing them. Singletons break one of the assumptions made when using the standard pattern of Arrange-Act-Assert. The Arrange step assumes a clean starting point for every test. Each test should be completely independent of every other test. But by design, singletons retain state. In the case of unit tests, this means that one test can leave state altered that might affect another test, and that’s not good.

A pattern that can be used to make singletons more easily testable is called Singleton By Choice. In this pattern, singletons can be created and used as described by the singleton design pattern, but without forcing the use of singleton behavior upon the tests.

By design, Singletons are intended to be accessed using the sharedInstance class method, or sometimes a similarly named class method like shareManager. But what happens if the Singleton class is created using the usual alloc init approach? (eg. [[MySingleton alloc] init]).

In Objective-C, it is common to see Singletons implemented in a manner that prevents making this mistake. This is done by overriding allocWithZone or init and redirecting back to the sharedInstance class method.

Before doing that though, stop and think about why you are doing it.  While well intentioned, this type of behavior not only runs contrary to the TDD approach, it in fact makes it difficult to write unit tests. The assumption is that “some other programmer is going to mistake this singleton for a normal object, so let’s fix this potential future error by silently redirecting the wrong method call to the right one”.

Ok, lots of problems with that assumption, including:

  • New code won’t be done using unit tests
  • New code may break existing code, and the existing unit tests won’t catch it
  • or maybe existing unit tests won’t ever be run
  • I’m the only programmer that will work on this code that knows what I’m doing, so I better fix the other programmer’s mistakes now, while I can.

In a TDD world, we really don’t need to write code that is this defensive. It’s actually kind of offensive, pun intended.

Go ahead and create a singleton when appropriate, use the sharedInstance method in all your TDD unit tested code, and use [[mySingleton alloc] init] in your unit tests to get a fresh copy to test.

And finally, you’re using a simple singleton parent class so that you don’t have to rewrite it and its unit tests every time, right? If not, here’s one, or you can get it from Github:

//
//  SingletonByChoice.h
//
//  This class provides basic Singleton By Choice functionality.
//  It is intended for use as a parent class for singletons.

#import <Foundation/Foundation.h>

@interface SingletonByChoice : NSObject

+ (SingletonByChoice *)sharedInstance;

@end
//
//  SingletonByChoice.m
//
//  This class provides basic Singleton By Choice functionality.
//  It is intended for use as a parent class for singletons.
//

#import "SingletonByChoice.h"

@implementation SingletonByChoice

+ (SingletonByChoice *)sharedInstance
{
    static dispatch_once_t onceToken = 0;
    
    __strong static id _sharedObject = nil;
    dispatch_once(&onceToken, ^{
        _sharedObject = [[self alloc] init];
    });
    return _sharedObject;
}

@end
//
//  SingletonTests.m
//

#import <XCTest/XCTest.h>
#import "SingletonByChoice.h"

@interface SingletonTests : XCTestCase
@end

@implementation SingletonTests

#pragma mark - helper methods

- (SingletonByChoice *)createUniqueInstance {

    return [[SingletonByChoice alloc] init];
    
}

- (SingletonByChoice *)getSharedInstance {
    
    return [SingletonByChoice sharedInstance];
    
}

#pragma mark - tests

- (void)testSingletonSharedInstanceCreated {
    
    XCTAssertNotNil([self getSharedInstance]);

}

- (void)testSingletonUniqueInstanceCreated {
    
    XCTAssertNotNil([self createUniqueInstance]);
    
}

- (void)testSingletonReturnsSameSharedInstanceTwice {
    
    SingletonByChoice *s1 = [self getSharedInstance];
    XCTAssertEqual(s1, [self getSharedInstance]);
    
}

- (void)testSingletonSharedInstanceSeparateFromUniqueInstance {
    
    SingletonByChoice *s1 = [self getSharedInstance];
    XCTAssertNotEqual(s1, [self createUniqueInstance]);
}

- (void)testSingletonReturnsSeparateUniqueInstances {
    
    SingletonByChoice *s1 = [self createUniqueInstance];
    XCTAssertNotEqual(s1, [self createUniqueInstance]);
}

@end

Need-Driven Development

I recently came across a 10 year old article titled “Mock Roles, not Objects“. I recall reading this article at some time in the past, but I guess that I really didn’t get it the first time. This article is awesome, and I recommend reading it. But maybe like me, you need to become fairly familiar with TDD and using mock objects before it will have much impact on your thinking.

When I reread the article, I was immediately struck by the opening 2 sentences:

“Mock Objects is an extension to Test-Driven Development that supports good Object-Oriented design by guiding the discovery of a coherent system of types within a code base. It turns out to be less interesting as a technique for isolating tests from third-party libraries than is widely thought.”

Wow. The article then goes on to explain how the proper use of Need-Driven Development can help in the creation of good, clean architecture. I think that maybe the first time I read this article, I was still stuck in creating relatively few, large objects with lots of methods. After repeated viewings of Uncle Bob’s videos though, I changed my mindset to using lots of small, SRP compliant objects. Without that mindset, it might be possible to misinterpret this article as recommending the use of partial mocks to intercept calls from one method to another within the same object. Been there, done that, got help and am hopefully on the road to recovery now.

I think that there are still parts of this article that I’m not evolved enough yet to fully grok, for example section 4.1. I think I’ll come back and reread the article in another year or so and see if I get it then.

How is everybody liking XCTest?

Ok, Xcode 5 and XCTest have been out for awhile now. What has been your experience with it?

I’ve used it for some simple projects, doing TDD with it. I’ll have more to say about that in the near future. But I have to say that I really like the tighter integration of unit testing into Xcode.

One of my favorite things is the speed at which I can perform the TDD loop. This has been boosted by a couple things: the test classes assistant, and the ability now to rerun a single test by clicking on the symbol next to the line number (red “x” for a failing test, green checkmark for a passing test).

In the screen capture below, I’m using the test classes assistant to display the associated unit test case file on the right. I’ve just run tests, and the test at line 37 on the right failed, and is highlighted. Note that the other tests have a green checkmark next to them.

Although you can’t tell from this screen capture, I’m hovering the mouse over the red checkmark left of the line number at line 37, which has caused the checkmark to switch to a right arrow. Clicking on this reruns just that test. How cool is that!

Xcode test assistant
XCTest example of failing test

XCTest Assertions

XCTest supports the following assertions. This list is essentially the same as the list of assertions supported by SenTestingKit, but with the “ST” prefix changed to “XCT”.

  • XCTFail (format…)
  • XCTAssertNil (a1, format…)
  • XCTAssertNotNil (a1, format…)
  • XCTAssert (a1, format…)
  • XCTAssertTrue (a1, format…)
  • XCTAssertFalse (a1, format…)
  • XCTAssertEqualObjects (a1, a2, format…)
  • XCTAssertEquals (a1, a2, format…)
  • XCTAssertEqualsWithAccuracy (a1, a2, accuracy, format…)
  • XCTAssertThrows (expression, format…)
  • XCTAssertThrowsSpecific (expression, specificException, format…)
  • XCTAssertThrowsSpecificNamed (expression, specificException, exceptionName, format…)
  • XCTAssertNoThrow (expression, format…)
  • XCTAssertNoThrowSpecific (expression, specificException, format…)
  • XCTAssertNoThrowSpecificNamed (expression, specificExcepton, exceptionName, format…)

Refer to XCTestAssertions.h. The easiest way to locate this file is to Ctrl+Click on an assertion (such as “STFail”), and select “Jump to Definition”.

 

XCTest First Impressions

One of the big improvements in Xcode 5 is unit testing. Woohoo!!!

  • A Test Navigator has been added.
  • The Assistant Editor provides new test categories that place code and tests side by side.

SenTestingKit has been replaced with XCTest. This appears to be essentially just a renaming of SenTestingKit to XCTest, with assertions renamed from ST* to XCT*. For example, STAssertTrue is now XCTAssertTrue.

Also, the previous concept of “Logic” and “Application” tests is gone. By default, all tests are now “application tests”.

The Xcode 5 templates also do not provide an option to “Include unit tests”. Unit tests are always included. And there is an option in Xcode 5 to convert existing OCUnit (SenTesting) projects to use XCTest, making it easy to make the change.

I guess unit testing is finally a first class citizen in iOS development now.

Using Storyboards

We generally do not use Storyboards in our projects where I work. That’s mainly because, at least as of iOS 6, they cannot be merged. This means that only a single developer can make changes to a storyboard at any given time. However, it appears that Apple is working on correcting this situation. So I’ve been taking another look at Storyboards, and investigating how these can be integrated into our best practices (TDD, clean code and architecture).

The first question that I had was “how to unit testing view controllers loaded from a storyboard?”. This appears to be fairly easy, and I found the answer on Rafael Adson’s blog. Simply load the view controller from the storyboard in the test setUp method:

- (void)setUp
{
    [super setUp];
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    self.vc = [storyboard instantiateViewControllerWithIdentifier:@"main"];
    [self.vc performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];
}

You’ll need to set the identifier for the view controller in the storyboard, and then modify the code above to match the name of your storyboard and the id you chose for the view controller.

The second question is how to integrate storyboards into the VIPER architecture. First off, it seems that the storyboard should replace the wireframe component. But there’s a problem. Normally the wireframe would be responsible for creating the view controller, interactor, and presenter. But the storyboard only creates a view controller.

Now, we can easily have the view controller’s viewDidLoad method create the interactor and presenter, but that results in making the view controller aware of those components. From a clean architecture perspective, we’d prefer that these components remain as detached from each other as possible.

That said, I don’t know how the storyboard can be used to create all three components, so I guess we’re stuck with the viewDidLoad hack at this point. I’m continuing to investigate, and I’d love to hear your suggestions.