2
\$\begingroup\$

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 tear-down 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.

\$\endgroup\$

closed as off-topic by Jamal Nov 22 '15 at 19:30

This question appears to be off-topic. The users who voted to close gave this specific reason:

If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ I just realized that my method name could be read: "Fire Class as..." but it's intended as "Fire Class A's..." \$\endgroup\$ – edelaney05 May 2 '12 at 1:31
2
\$\begingroup\$

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
}
\$\endgroup\$

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