How Many Emcees Must Get Dissed…?


So you’re a programmer?
Yes, yes, yes, yes, yes…
I code very deeply…
In about 4 seconds, a teacher will begin to speak
I code very deeply…

Hi, I’m Cliff. You’re here because you wish to know the answer to the age old question, a question that has been asked by many different people over the ages. (Yes some even asked this very question on Yahoo.) This question is generally understood to be it’s own answer. However, in today’s discussion we will revisit the question in code using test driven design as a unique experiment uncover the answer.

Before we begin, some elaboration is in order for those who do not have a background in heavy beats and sentences terminated by rhyming vocabulary. The question we must ask and answer is, “How many emcees must get dissed before somebody says don’t mess with Cliff?” (Astute readers will note that I am paraphrasing slightly from the original.) We ask this question because over the years there have been many emcees who have had their egos reduces to nothing merely because they didn’t understand the exact count or quantitative answer. “Is it 10, or 11? Was my number twelve? Oh, I lost my ticket!”, they would say on their way towards total annihilation. “What is an emcee?”, you ask. It is the phonetic spelling of one MC which (as Queen Latifah explains) is short for “Microphone Commando”. I digress…

So we begin with an iPhone Xcode 4.2 project which will generate an iOS 5.0 static library. Our end result will be a static library which will be able to be linked to any iPhone app that wishes to provide an answer to this very question. Please open you copy of Xcode and follow along. I named my Project “HowManyEmcees” and created it as a static lib project sacked in my “dev” folder. (Feel free to save your project where ever you see fit.) Make sure you have the “Include UnitTests” checkbox marked and also check the “Create a local git repository” option to enable source control. For some it will be difficult but it’s very important that you follow each step exactly. The idea is to recondition yourself from writing code before you know why you’re writing code. Our first test will be as follows.

//
//  HowManyEmceesTests.m
//  HowManyEmceesTests
//
//  Created by Clifton Craig on 12/27/11.
//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//

#import
#import "EmceeWarehouse.h"

@interface HowManyEmceesTests : SenTestCase

@end

@implementation HowManyEmceesTests

- (void)setUp
{
    [super setUp];

    // Set-up code here.
}

- (void)tearDown
{
    // Tear-down code here.

    [super tearDown];
}

- (void)testCanGenerateListOfEmcees
{
}

@end

We start by declaring the need for an “EmceeWarehouse” and beginning a test case that will eventually demonstrate how we get a list of Emcees from the EmceeWarehouse. That said, you can expect a warehouse full of emcees to get dissed in our example. Let’s continue with typical memory management etiquette assigning the warehouse in our setUp and clearing it in our teardown. (I could skip this step if I were to use the new iOS 5.0 ARC features but let’s keep things familiar and not introduce too much at once here.)

@interface HowManyEmceesTests : SenTestCase

@property (nonatomic, retain) EmceeWarehouse *emceeWarehouse;

@end

@implementation HowManyEmceesTests
@synthesize emceeWarehouse;

- (void)setUp
{
    [super setUp];

    self.emceeWarehouse = [[EmceeWarehouse alloc] init];
}

- (void)tearDown
{
    self.emceeWarehouse = nil;

    [super tearDown];
}

Now we hit Cmd+U, compile, crash, and understand that we need to actually have an EmceeWarehouse. It is very important that you compile at this point and verify all of the compile errors. If you are not getting compile errors then it is likely that you have not resisted the urge to correct the red flags in your editor as you went ahead and implemented the code before writing your test. If you don’t have errors, go back and delete your premature EmceeWareHouse, compile and crash. After crashing on compile create a plain old Obj-C object named EmceeWarehouse and add it only to the library (not the test) target. Compile, run (Cmd+U) and get your first green bar or passing test suite. (Why doesn’t Xcode unit testing feature green bars?) Back in our test method we express our intentions. This is the fun part because we can be as creative as we wish. I like to think of it as programming with the most dynamic language on the planet because you can change the way methods work as you write them without the penalty of recompiling or bug fixing. You’ll see what I mean in a moment.

- (void)testCanGenerateListOfEmcees
{
    id manyEmcees = [self.emceeWarehouse listOfEmcees];

    STAssertTrue([[manyEmcees class] isSubclassOfClass:[NSArray class]],
                 @"Expecting an instance of NSArray but got %@", [[manyEmcees class] description]);
}

Add the logic above to your test and compile, run and crash. If you didn’t crash you jumped ahead again. Delete your listOfEmcees implementation in “EmceeWarehouse.m” and start over. Note that the amount of code you delete and add later is directly proportional to the amount of unnecessary code found in projects that do not use TDD. As we iterate between test and code we are looking for the simplest thing that works which is the barest amount of code that can satisfy the most recent test, anything more will absolutely degrade the quality of your project. This will make more sense after a few iterations. Let’s look back at our test. It doesn’t quite fit what we are after. We are looking to get many different emcees from the warehouse, possibly all with varying skill levels. Lets change the test to make it more indicative of that.

- (void)testCanAskForManyDifferentEmcees
{
    id manyDifferentEmcees = [self.emceeWarehouse manyDifferentEmcees];

    STAssertTrue([[manyDifferentEmcees class] isSubclassOfClass:[NSArray class]],
                 @"Expecting an instance of NSArray but got %@", [[manyDifferentEmcees class] description]);
}

This contrived example illustrates an important point. Making changes to your tests should cost little if anything which highlights another valuable concept. Making changes to your product’s requirements should cost very little. In TDD, a test should be the direct result of a discussion of some product requirement and should closely follow or even echo a given requirement. Requirements change frequently because they cannot be completely understood until your product ages a little, so formalizing the requirements in test code is important. Also, if you are being penalized for changing your requirements then you will be less likely to make the necessary changes in your requirements thus suffocating your product. Now lets implement our warehouse just a little.

//  EmceeWarehouse.h
#import

@interface EmceeWarehouse : NSObject

-(id) manyDifferentEmcees;

@end

//  EmceeWarehouse.m
#import "EmceeWarehouse.h"

@implementation EmceeWarehouse

-(id)manyDifferentEmcees
{
    return nil;
}

@end

Cmd+U and crash! It’s like stumbling through a dark room with your hands extended reaching for the wall or nearby objects. We want to carefully approach our optimal solution without stepping passed it. The less code we use to solve a problem or requirement, the more power/flexibility we have to change existing requirements or add new ones. We need to make one minor change in “EmceeWarehouse.m” before we can move on.


-(id)manyDifferentEmcees
{
    return [NSArray array];
}

Now that we pass we can commit. Committing after every passing test is a good idea because it leaves a nice audit trail of how you grew your solution. One thing to note now is the return type of our method. Even though our test eventually compared the manyDifferentEmcees to an NSArray we didn’t formalize the return type in the method declaration. That optimization must be done after we go over the test a few times and get our initial pass because it allows us the time to mentally settle on the appropriate types. Once again, the barest amount of code to satisfy the requirement is what’s important. Then you go back and refine the requirements and finally the implementation. Looking back at our test we can ask ourselves, do we really want an NSArray or should it be an NSSet? Should we try to use an NSMutableArray? We’ll stay with NSArray since we probably want to go through emcees in a predictable order and also since each emcee will probably want to listen to the one before him. We’ll forgo the use of NSMutableArray externally because we don’t have a need to change the order or members of the array. That decision is difficult for some people because, as engineers, we like to expound on what we believe the optimal solution to be rather than allowing proper iteration to guide us. That is, we love to make those “experienced educated guesses” at what would most likely solve the problem. Making such assumptions (corruptions) is the actual equivalent of writing bugs into your software or pouring salt into the open wound. Let’s make the NSArray return type formal at this point run our tests to make sure they still pass and commit.

//  EmceeWarehouse.h
#import

@interface EmceeWarehouse : NSObject

-(id) manyDifferentEmcees;

@end

//  EmceeWarehouse.m
#import "EmceeWarehouse.h"

@implementation EmceeWarehouse

-(id)manyDifferentEmcees
{
    return [NSArray array];
}

@end

In our next requirement we explore how many emcees we’re prepared to devour. Recall above that we merely satisfied the requirement of being able to ask for emcees from the warehouse, now we need to count them and make sure we have enough to prove our street credibility. Pay attention to the fact that we are incrementally exploring what our requirements should be and using that to determine what code to write and how it should look. Add the next test.


-(void)testManyDifferentEmceesShouldIncludeMoreThanOne
{
    NSInteger howManyEmcees = [[self.emceeWarehouse manyDifferentEmcees] count];
    STAssertTrue(howManyEmcees > 1, @"Should have more than 1 emcee to prove street credibility. we count %i", howManyEmcees);
}

Compile, test, fail and fix the implementation to the simplest thing that works.


-(NSArray*)manyDifferentEmcees
{
    return [NSArray arrayWithObjects:@"This is not and Emcee",
            @"and neither is this",
            nil];
}

I like to push the needle towards stoopid sometimes as I add code that is obviously wrong. The point here is to force yourself to write the tests (or the “requirements” as I like to think of them) to expose the obviously wrong or missing code. We now have passing tests and an opportunity to commit. Commit the above changes and let’s move on to one of my favorite parts. Now it’s time to define what it means to be an actual “Emcee”. See the next code example.

-(void)testEachOfManyDifferentEmceesShouldBeAnActualEmcee
{
    for (id eachEmcee in [self.emceeWarehouse manyDifferentEmcees]) {
        //What do we do here???
    }
}

A heavy-handed approach would be to compare eachEmcee class against an actual class named “Emcee”. Since we haven’t thought that far in advance lets try something simple yet not so obvious.

    for (id eachEmcee in [self.emceeWarehouse manyDifferentEmcees]) {
        STAssertTrue([eachEmcee conformsToProtocol:@protocol(Emcee)],
                     @"How many emcees wanna be emcees? Never be emcees. Cause they can't emcee!");
    }

Protocols are mere abstractions that don’t need an implementation (similar to Java interfaces) and they are simple enough to allow us to specify our intention while allowing us to move on with developing the warehouse. I hit Cmd+U (you should too) after even the most minute source changes. It is a means of giving you instant feedback of what you did as well as how what you just did impacts the overall project. Our compiler (and our editor both) tells us we need to define the Emcee protocol. Now, towards the top of “EmceeWarehouse.h” add:

@protocol Emcee

@end

Cmd+U exposes the silly behavior in our warehouse. We need something that represents a real emcee so we’ll add the simplest thing that works. Put thins anywhere in your project (I included it at the bottom of “EmceeWarehouse.m”) to allow our test to pass:

@interface NSString(Emcee)
@end

@implementation NSString(Emcee)

@end

This is a category used only from within EmceeWarehouse.m so it does not need to have separate “.m” and “.h” files. I also like to patch up my implementation to reflect the new change:


-(NSArray*)manyDifferentEmcees
{
    return [NSArray arrayWithObjects:@"I wanna be an Emcee!",
            @"Now I'm an Emcee!",
            nil];
}

As you can see, right now it doesn’t take much to be an emcee. Make sure you commit your changes before moving on. Let’s make our warehouse just a little more interesting by including a capacity and making sure it’s always full to capacity.
HowManyEmceesTests.m

@interface HowManyEmceesTests : SenTestCase

@property (nonatomic, retain) EmceeWarehouse *emceeWarehouse;
@property (nonatomic, assign) NSInteger warehouseCapacity;

@end

@implementation HowManyEmceesTests
@synthesize emceeWarehouse;
@synthesize warehouseCapacity;

- (void)setUp
{
    [super setUp];
    self.warehouseCapacity = 100;
    self.emceeWarehouse = [[EmceeWarehouse alloc] initWithCapacity:self.warehouseCapacity];
}

Cmd+U crash with unrecognized selector “initWithCapacity:” sent to instance.
EmceeWarehouse.h

@interface EmceeWarehouse : NSObject

- (id)initWithCapacity:(NSInteger)aCapacity;
-(NSArray*) manyDifferentEmcees;

@end

EmceeWarehouse.m


- (id)initWithCapacity:(NSInteger)aCapacity /*we don't wanna do anything with the capacity yet!!!*/
{
    self = [super init];
    if (self) {
        //don't touch this part yet!!!
    }
    return self;
}

Cmd+U, pass, commit! Now we need to make sure we’re always full to capacity.
HowManyEmceesTests.m


-(void)testWarehouseIsFullToCapacity
{
    STAssertEquals((NSInteger)[[self.emceeWarehouse manyDifferentEmcees] count], self.warehouseCapacity,
                   @"EmceeWarehaouse should be full to warehouseCapacity");
}

Cmd+U fail, and fix:
EmceeWarehouse.h

@interface EmceeWarehouse : NSObject

- (id)initWithCapacity:(NSInteger)aCapacity;
-(NSArray*) manyDifferentEmcees;

@property(nonatomic, assign) NSInteger capacity;

@end

EmceeWarehouse.m

@implementation EmceeWarehouse
@synthesize capacity;

- (id)initWithCapacity:(NSInteger)aCapacity
{
    self = [super init];
    if (self) {
        self.capacity = aCapacity;
    }
    return self;
}

-(NSArray*)manyDifferentEmcees
{
    NSMutableArray *soManyDifferentEmcees = [NSMutableArray array];
    for (int countOfEmcees = 0; countOfEmcees < self.capacity; countOfEmcees++) {
        [soManyDifferentEmcees addObject:@"I wanna be an emcee!!"];
    }
    return soManyDifferentEmcees;
}

@end

Now when you have so many different emcees there’s bound to be a sucka in the mix! To be specific, a sucka emcee is the one that will cop out in the midst of a challenge. He’s the one that has no heart and is afraid to battle- the one emcee that’ll say “Don’t mess with Cliff”, thus terminating what eventually become an infinite loop, or never ending round of battles. Many people don’t understand what exactly does it mean to be an emcee. Our next round of tests will explain it very clearly. At the very bottom of “HowmanyEmceesTests.m” we’ll add:

#pragma mark - EmceeTests

@interface EmceeTests : SenTestCase

@property (nonatomic, retain) id theEmcee;
@end

@implementation EmceeTests
@synthesize theEmcee;

-(void)setUp
{
    [super setUp];
}

-(void)tearDown
{
    self.theEmcee = nil;
    [super tearDown];
}

@end

It’s more common to use different test case files but for our purposes we can stuff everything in one file. Hit Cmd+U and verify both test case classes show as being executed. (You can commit here as well.) Add our first definition of a real emcee:
HowManyEmceesTests.m

-(void)testAnEmceeCanBeASucka
{
    self.theEmcee.isSucka;
}

Cmd+U (after every little change, remember?) forces us to go add a property that will expose the true heart of each emcee, showing if he’s hardcore or not. (Yeah, yeah, I know! Our emcee could be a “she” as well! and she could also be hardcore.) Let’s define that property.
EmceeWarehouse.h

@protocol Emcee

@property (nonatomic, readonly) BOOL isSucka;

@end

Adding the following to the NSString category in “EmceeWarehouse.m” will eventually give us a warehouse full of sucka emcees:
EmceeWarehouse.h

@implementation NSString(Emcee)
@dynamic isSucka;

-(BOOL)isSucka
{
    return YES;
}

Cmd+U, pass and commit. We’re at a point where we should break out two different classes of emcees. Enter ChampionEmcee and SuckaEmcee, both of these will probably extend the base class of Emcee, right? Before we get ahead of ourselves let’s start from the tests since we have some cleanup to do. First create an empty file named EmceeWarehouse.m and cut/paste the following from “HowManyEmceesTests.m” (copy the imports of course but cut everything else):
HowManyEmceesTests.m

#import
#import "EmceeWarehouse.h"

#pragma mark - EmceeTests

@interface EmceeTests : SenTestCase

@property (nonatomic, retain) id theEmcee;
@end

@implementation EmceeTests
@synthesize theEmcee;

-(void)setUp
{
    [super setUp];
}

-(void)tearDown
{
    self.theEmcee = nil;
    [super tearDown];
}

-(void)testAnEmceeCanBeASucka
{
    self.theEmcee.isSucka;
}

@end

Commit after verifying that this new test case is associated with the tests target and its tests are still being executed. (In my case I believe it defatted to the test target since that was the active target.) Change #import "EmceeWarehouse.h" to #import "Emcee.h". Compile (Cmd+U), crash, and know that you now need to create and Emcee Objective-C file. Pay attention to how we always use the Cmd+U sequence to let either the compiler or the runtime let us know when we need to add more code. It is a difficult habit to build, breaking out of the “I know I need to do X” mentality. The more frequently you use Cmd+U the more you get used to seeing what doesn’t work while familiarizing with all the different errors that would otherwise confuse you. Add the new “Emcee” class and Cmd+U again to learn that you now need to move the protocol definition out of “EmceeWharehouse.h”.
EmceeWharehouse.h

#import "Emcee.h"
@protocol Emcee

@property (nonatomic, readonly) BOOL isSucka;

@end

Change the setUp to make sure we are exercising the Emcee class:

-(void)setUp
{
    self.theEmcee = [[[Emcee alloc] init] autorelease];
    [super setUp];
}

Cmd+U will show failure since we never declared/synthesized the isSucka property. Make the Emcee class follow the Emcee protocol and synthesize the isSucka property.

@interface Emcee : NSObject <Emcee>

@end

@implementation Emcee
@synthesize isSucka;

@end

Test (Cmd+U), pass and commit. Change the EmceeWarehouse manyDifferentEmcees method to use our new class and remove the ad-hoc category on NSString.

-(id<Emcee>)anyOldEmcee
{
    return [[[Emcee alloc] init] autorelease];
}

-(NSArray*)manyDifferentEmcees
{
    NSMutableArray *soManyDifferentEmcees = [NSMutableArray array];
    for (int countOfEmcees = 0; countOfEmcees < self.capacity; countOfEmcees++) {
        [soManyDifferentEmcees addObject:[self anyOldEmcee]];
    }
    return soManyDifferentEmcees;
}

Cmd+U, pass, commit. Now add the following to the bottom of the EmceeTests file:

@interface SuckaEmceeTests : EmceeTests

@end

@implementation SuckaEmceeTests

-(void)setUp
{
    [super setUp];
    self.theEmcee = [[[SuckaEmcee alloc] init] autorelease];
}

-(void)testASuckaEmceeWillAlwaysBeASucka
{
    STAssertTrue(self.theEmcee.isSucka, @"A sucka emcee will always be a sucka!");
}

@end

Hitting Cmd+U makes our toolchain inform us that we need to define a SuckaEmcee:
Emcee.h

@interface SuckaEmcee : Emcee

@end

Emcee.m

@implementation SuckaEmcee

@end

Hitting Cmd+U once more reminds us that a sucka emcee will always be a sucka. We must code our new class this way.
Emcee.m

@implementation SuckaEmcee

-(BOOL)isSucka
{
    return YES;
}

@end

Cmd+U, pass, and commit. Back in our EmceeTests class. We need to make our Emcees spit a lil’ sumn sumn.

-(void)testAnEmceeCanSpitSomething
{
    NSString *aLilSumnSumn = [self.theEmcee spitSomething];
    STAssertNotNil(aLilSumnSumn, @"aLilSumnSumn should never be nil because that's just not John Blaze. Y'know?");
}

Cmd+U makes us add the following:

@protocol Emcee <NSObject>

@property (nonatomic, readonly) BOOL isSucka;

-(NSString *)spitSomething;

@end

//And in our base class
@implementation Emcee
@synthesize isSucka;

-(NSString *)spitSomething
{
    return nil;
}

@end

Cmd+U shows two errors for the one test. This is important to pay attention to since we’ve added the expectation to the base test case for emcee it will apply to all Emcees, even the suckas. Add the simplest thing that works:

-(NSString *)spitSomething
{
    return @"I'll battle any sex or any race. You beating me is like Billy Crystal playing Scarface.";
}

That’s not necessarily the simplest thing but it’s colorful enough to get the point across. Now let’s see what happens when one Emcee spits something at another.

-(void)testAnEmceeCanSpitSomethingAtAnother
{
    id<Emcee> anotherEmcee = [[[Emcee alloc] init] autorelease];
    NSString *aLilSumnSumn = [self.theEmcee spitSomething:anotherEmcee];
    STAssertNotNil(aLilSumnSumn, @"aLilSumnSumn should never be nil because that's just not John Blaze. Y'know?");
}

Editor and compiler errors tell us we need to do something, something simple.

-(NSString *)spitSomething:(id<Emcee>)anotherEmcee
{
    return nil;
}

Cmd+U runs and shows us where we’re not so John Blaze. A simple fix would be to make the new spitSomething: work like the other.

-(NSString *)spitSomething:(id<Emcee>)anotherEmcee
{
    return [self spitSomething];
}

Now we’re going to see an Emcee just getting dissed.

-(void) testAnEmceeJustGotDissed
{
    [[[[Emcee alloc] init] autorelease] spitSomething:self.theEmcee];
    self.theEmcee.justGotDissed;
}

Cmd+U compile error tells us to add the missing method.
Emcee.h

@protocol Emcee <NSObject>

@property (nonatomic, readonly) BOOL isSucka;
@property (nonatomic, assign) BOOL justGotDissed;

Emcee.m

@implementation Emcee
@synthesize isSucka;
@synthesize justGotDissed;

Now add our expectation to our test case.

-(void) testAnEmceeJustGotDissed
{
    [[[[Emcee alloc] init] autorelease] spitSomething:self.theEmcee];
    STAssertTrue(self.theEmcee.justGotDissed, @"The Emcee just got dissed. Property check should confirm this.");
}

The simplest solution:

-(BOOL)justGotDissed
{
    return YES;
}

Of course it shouldn’t make sense. We want to write another test that explains why tour current solution is less than optimal:

-(void)testAnEmceeDidNotJustGetDissed
{
    STAssertFalse(self.theEmcee.justGotDissed, @"Nobody spit anything at the emcee. He did not just get dissed");
}

All along we are adding documentation along side our new software components. This is the best kind of documentation because it is executable and always up to date. When something breaks we have documentation explaining how we expect things to work and where the breakage is. Removing the formal definition from our implementation will cause it to fall back on the synthesized definition which will always return NO when we asked if we just got dissed. That will fix our latest test but break our earlier test. We are forced to change the state of the other emcee as we spit something at another emcee.

-(NSString *)spitSomething:(id<Emcee>)anotherEmcee
{
    anotherEmcee.justGotDissed = YES;
    return [self spitSomething];
}

//Remove/delete this...
//-(BOOL)justGotDissed
//{
//    return NO;
//}

Cmd+U to validate our above changes then feel free to delete the commented code. Cmd+U can be used to confirm our refactoring did not alter behavior prior to commiting. Congratulations! We have demonstrated how an emcee just got dissed. We’re almost there! A couple more changes are in order. First we need to elaborate on the SuckaEmcee. You see, a SuckaEmcee should say “Don’t mess with Cliff!” after he just got dissed because he’s soft. We need to add a test to document and demonstrate the idea. At the bottom of EmceeTests.m inside our SuckEmceeTests add the following:
EmceeTests.m

-(void) testASuckaEmceeJustGotDissed
{
    [[[[Emcee alloc] init] autorelease] spitSomething:self.theEmcee];
    STAssertEqualObjects([self.theEmcee spitSomething], @"Don't mess with Cliff!",
                   @"We expect the SuckaEmcee to be soft and tell others 'Don't mess with Cliff'.");
}

Notice how we really don’t need comments to explain what’s happening. Our expectations are inlined as code and our logic echoes our use cases. We can use the simplest thing that works here. At the bottom of Emcee.m in the @implementation for SuckaEmcee, we add this:

-(NSString *)spitSomething
{
    return @"Don't mess with Cliff!";
}

The SuckaEmcee feels slightly incomplete. We want our sucka to try to spit lil’ something on occasion, because he is an Emcee after all. The best way to demonstrate this is in our definition of an Emcee in the EmceeTests. We add a new test.

-(void) testAnEmceeWhoHasNotJustGotDissedWillNotShowFear
{
    STAssertFalse(self.theEmcee.justGotDissed, @"Given an Emcee that has not yet been dissed...(sanity check.)");
    STAssertFalse([[self.theEmcee spitSomething] isEqualToString:@"Don't mess with Cliff!"],
                  @"Any emcee will try to spit something before being dissed.");
}

Our test is showing us how our SuckaEmcee is not really behaving as an Emcee should. In plain slanglish, our current emcee is always punking out. We have to fix this since our sucka has “Emcee” in it’s class name and in it’s class hierarchy. In our SuckaEmcee @implementation we make it try to spit something before getting dissed:

-(NSString *)spitSomething
{
    if (self.justGotDissed) {
        return @"Don't mess with Cliff!";
    } else {
        return [super spitSomething];
    }
}

Cmd+U pass, and commit. Now there’s still an issue with the each Emcee in our EmceeWarehouse. Among every group of Emcees you’re bound to find a sucka. Lets add one more test to our HowManyEmceesTests to make sure.

-(void)testThereShouldBeASuckaEmceeInTheWarehouse
{
    NSUInteger locationOfSucka = [[self.emceeWarehouse manyDifferentEmcees] indexOfObjectPassingTest:^BOOL(id eachEmcee, NSUInteger eachIndex, BOOL *stop){
        return eachEmcee.isSucka;
    }];
    STAssertFalse(locationOfSucka == NSNotFound,@"Location of our SuckaEmcee should NOT be NotFound.");
}

Cmd+U and we see a new but expected failure from the assert we just coded. We need to change the implementation of our manyDifferentEmcees method using the most simplest approach:

-(NSArray*)manyDifferentEmcees
{
    NSMutableArray *soManyDifferentEmcees = [NSMutableArray array];
    for (int countOfEmcees = 0; countOfEmcees &lt; self.capacity; countOfEmcees++) {
        [soManyDifferentEmcees addObject:[self anyOldEmcee]];
    }
    [soManyDifferentEmcees addObject:[[[SuckaEmcee alloc] init] autorelease]]; //tack on a SuckaEmcee
    return soManyDifferentEmcees;
}

Cmd+U and guess what? We fixed our test and broke another. Our TestCases just deposited a few dollars into our development account! Our test, testWarehouseIsFullToCapacity, complains that our warehouse must be flu to capacity. We cannot just add a sucka on the end because he won't fit. For now we just tweak the manyDifferentEmcees method once again to make room.

-(NSArray*)manyDifferentEmcees
{
    NSMutableArray *soManyDifferentEmcees = [NSMutableArray array];
    for (int countOfEmcees = 0; countOfEmcees &lt; self.capacity - 1/* Leave room for desert, the last Emcee is gonna get ate!*/; countOfEmcees++) {
        [soManyDifferentEmcees addObject:[self anyOldEmcee]];
    }
    [soManyDifferentEmcees addObject:[[[SuckaEmcee alloc] init] autorelease]]; //tack on a SuckaEmcee
    return soManyDifferentEmcees;
}

At this point we've built enough infrastructure to answer our original question but this solution is a little boring and completely predictable since we always fill our warehouse with a sucka on the end. We have tests to cover our next move, refactor! We want to take a different approach to making sure there's a sucka emcee in the crowd. First restore our manyDifferentEmcees method to it's original form. Revert the EmceeWarehouse.m file so it looks like this:

-(NSArray*)manyDifferentEmcees
{
NSMutableArray *soManyDifferentEmcees = [NSMutableArray array];
for (int countOfEmcees = 0; countOfEmcees &lt; self.capacity; countOfEmcees++) {
[soManyDifferentEmcees addObject:[self anyOldEmcee]];
}
return soManyDifferentEmcees;
}

Cmd+U and fail on the original assert failure. Next we change the anyOldEmcee method to return either an Emcee or a sucka based on a random value:

-(id)anyOldEmcee
{
if ((arc4random() % 100) &gt; 95) {
return [[[SuckaEmcee alloc] init] autorelease];
} else {
return [[[Emcee alloc] init] autorelease];
}
}

That should give us a rough distribution of 5% sucka Emcees in our warehouse. We can adjust the percentage to our liking as our needs mature. For now this will do. Our last bit of code is intended to test each emcee. Create the following test case:

-(void)testHowManyEmceesMustGetDissed
{
    id cliff = [[[Emcee alloc] init] autorelease];
    NSUInteger locationOfEmceeThatJustGotDissed = [[self.emceeWarehouse manyDifferentEmcees] indexOfObjectPassingTest:^BOOL(id eachEmcee, NSUInteger eachIndex, BOOL *stop){
        [cliff spitSomething:eachEmcee];
        *stop = [[eachEmcee spitSomething] isEqualToString:@"Don't mess with Cliff!"];
        return *stop;
    }];
    STAssertEquals(locationOfEmceeThatJustGotDissed, (NSUInteger)0,@"%i emcees just got dissed", locationOfEmceeThatJustGotDissed + 1);
}

You may want to run this test more than once. It illustrates an important technique I often use when doing TDD. It’s what I call the discovery test. Whenever I have an API call that produces an unknown (or in this case variable) value that I want to discover, I write a failing test which outputs the value of the API call as part of the assert description. In the above example we finally discover how many emcees just got dissed, before somebody says, “don’t mess with Cliff!” I leave you to mull over it for a while. Leave comments if you will. G’night.

1 Comment

  1. bob

    [[manyEmcees class] isSubclassOfClass:[NSArray class]]

    should be written as

    [manyEmcees isKindOfClass:[NSArray class]]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 251 other followers

%d bloggers like this: