Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have found that writing unit tests helps me to discover both the intent and also reusability of code that I'm working on. It has been generally helpful in helping develop my "code smell."

I was working on two unit tests that are basically mirror images, and realized that I was typing code that looked almost identical. I am hoping that someone could help me refactor the similar parts of these two unit tests into shared setup and teardown methods.

- (void)testBlockMethodOne
{
    NSError *expectedError = [NSError errorWithDomain:@"An error"
                                         code:42
                                     userInfo:nil];
    [mockClassA setError:expectedError];
    [mockClassA setRunCompletionBlockImmediately:YES];

    __block NSError *actualError = nil;
    __block id actualObject = nil;

    [classB fireClassAsBlockThenDoThisBlock:^(id object, NSError *error) {
        actualObject = object;
        actualError  = error;
    }];

    STAssertNil(actualObject, @"There was a problem");
    STAssertEqualObjects(actualError, expectedError, @"Got the expected error");
}

- (void)testBlockMethodTwo
{
    NSObject *expectedObject = [[NSObject alloc] init];
    [mockClassA setObject:expectedObject];
    [mockClassA setRunCompletionBlockImmediately:YES];

    __block NSError *actualError = nil;
    __block id actualObject = nil;

    [classB fireClassAsBlockThenDoThisBlock:^(id object, NSError *error) {
        actualObject = object;
        actualError  = error;
    }];

    STAssertNil(actualError, @"There was a problem");
    STAssertEqualObjects(actualObject, expectedObject, @"Got the expected object");
}

A little background about what's being tested: In production code, -fireClassAsBlockThenDoThisBlock is asynchronous. But, of course, in the unit tests I have a mock standing in for "Class A" that just calls the block immediately with either the error or object that I set on the mock.

I've repeated more setup code then I'd really like to between the two tests. My hope is once I see how someone else attacks this, I'll have gained some insight into refactoring block based methods.

share|improve this question
I just realized that my method name could be read: "Fire Class as..." but it's intended as "Fire Class A's..." – edelaney05 May 2 '12 at 1:31

1 Answer

up vote 2 down vote accepted

It looks like you can extract the method bodies entirely. Warning: I typed this code into a web browser, not an IDE:

- (NSDictionary *)invokeClassBBlockRunnerWithExpectedObject: (id)object expectedError: (NSError *)error {
    [mockClassA setObject: object];
    [mockClassA setError: error];
    [mockClassA setRunCompletionBlockImmediately: YES];

    __block NSError *actualError = nil;
    __block id actualObject = nil;

    [classB fireClassAsBlockThenDoThisBlock: ^(id object, NSError *error) {
        actualObject = object;
        actualError = error;
    }];

    NSMutableDictionary *results = [NSMutableDictionary dictionary];
    if (actualObject) [results setObject: actualObject forKey: @"Object"];
    if (actualError) [results setObject: actualError forKey: @"Error"];
    return results;
}

- (void)testBlockMethodOne {
    NSError *expectedError = [NSError errorWithDomain: @"An Error" code: 42 userInfo: nil];
    NSDictionary *results = [self invokeClassBBlockRunnerWithObject: nil error: expectedError];

    STAssertNil([results objectForKey: @"Object"], @"Shouldn't get an object when an error occurs");
    STAssertEqualObjects(expectedError, [results objectForKey: @"Error"], @"Should get the error I expected");
}

- (void)testBlockMethodTwo {
    id expectedObject = [[NSObject alloc] init];
    NSDictionary *results = [self invokeClassBBlockRunnerWithObject: expectedObject error: nil];

    // you get the idea
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.