Clean This Code

If you follow me on twitter @paytonrules you’ve probably seen me griping at various times about writing a testing framework for Objective-C.

I’m currently writing my first expectations, and bootstrapping a testing framework continues to be an interesting problem, which I’ll probably write more about later.

I find I continually have to go backwards on the tests to clean them, because as I implement more features I start using them in earlier tests. Meanwhile I want to clean the test I’ve recently written, often without the benefits of features I don’t have yet.

The code below are the tests for my first expectations. Currently I see a lot of duplication, and I’m curious how you might go about cleaning it with the framework in it’s current state. Keep in mind these rules:

  • I don’t have a before or after feature yet, and since some developers swear that using before and after is a bad practice, you can’t use that.

  • I don’t have expectations that take primitive types, which is why you see expect(obj) in some places, but a crude check then FAIL in others. I will, but don’t yet.

  • By using this DSL you don’t have access to a class to put variables on like you would in an XUnit style test. I have no idea of OCMock will work for mocking objects in my framework yet.

Given all those constraints—clean this code!

 1#import "OCDSpec/OCDSpec.h"
 2#import "OCDSpec/OCDSpecExpectation.h"
 3#import "OCDSpec/OCDSpecFail.h"
 4#import "Specs/Mocks/MockObjectWithEquals.h"
 5
 6CONTEXT(OCDSpecExpectation)
 7{
 8    describe(@"The Expecation",
 9             it(@"delegates beEqualTo to equalTo on the object its holding", 
10                ^{
11                    MockObjectWithEquals *actualObject = [[[MockObjectWithEquals alloc] init] autorelease];
12                    MockObjectWithEquals *expectedObject = [[[MockObjectWithEquals alloc] init] autorelease];
13
14                    OCDSpecExpectation *expectation = [[[OCDSpecExpectation alloc] initWithObject:actualObject inFile:@"" atLineNumber:0] autorelease];
15                    
16                    [expectation toBeEqualTo:expectedObject];
17                    
18                    [expect(actualObject) toBeEqualTo:expectedObject];
19
20              }),
21             
22             it(@"throws a failure when the objects aren't equal with an explanatory reason if the two objects are not equal to each other",
23                ^{
24                    MockObjectWithEquals *actualObject = [[[MockObjectWithEquals alloc] initAsNotEqual] autorelease];
25                    MockObjectWithEquals *expectedObject = [[[MockObjectWithEquals alloc] init] autorelease];
26                    
27                    OCDSpecExpectation *expectation = [[[OCDSpecExpectation alloc] initWithObject:actualObject inFile:@"" atLineNumber:0] autorelease];
28                    
29                    @try
30                    {
31                        [expectation toBeEqualTo:expectedObject];
32                        FAIL(@"Code did not throw a failure exception");
33                    }
34                    @catch (NSException *exception)
35                    {
36                        NSString *expectedReason = [NSString stringWithFormat:@"%@ was expected to be equal to %@, and isn't", actualObject, expectedObject];
37                        
38                        [expect([exception reason]) toBeEqualTo:expectedReason];
39                    }
40                }),
41             
42             it(@"throws a failure with the line and file passed in - i.e. uses OCDSpecFail", 
43                ^{
44                    MockObjectWithEquals *actualObject = [[[MockObjectWithEquals alloc] initAsNotEqual] autorelease];
45                    MockObjectWithEquals *expectedObject = [[[MockObjectWithEquals alloc] init] autorelease];
46                    
47                    OCDSpecExpectation *expectation = [[[OCDSpecExpectation alloc] initWithObject:actualObject inFile:@"FILENAME" atLineNumber:120] autorelease];
48                    
49                    @try
50                    {
51                        [expectation toBeEqualTo:expectedObject];
52                        FAIL(@"Code did not throw a failure exception");
53                    }
54                    @catch (NSException *exception)
55                    {
56                        [expect([[exception userInfo] objectForKey:@"file"]) toBeEqualTo:@"FILENAME"];
57                        
58                        if (![[[exception userInfo] objectForKey:@"line"] isEqual:[NSNumber numberWithLong:120]])
59                            FAIL(@"Should have had line number 120, didn't");
60                    }
61                    
62                }),
63             
64             it(@"Is created helpfully by the expect macro",
65                ^{
66                    NSObject *innerObject;
67                    
68                    OCDSpecExpectation *expectation = expect(innerObject);
69                    
70                    if (expectation.line != __LINE__ -2)
71                        FAIL(@"Line Number is wrong");
72                    
73                    [expect(expectation.file) toBeEqualTo:[NSString stringWithUTF8String:__FILE__]];
74                }),
75           nil);
76}
Eric Smith, Software Crafter

Eric Smith is a Principal Crafter at 8th Light Chicago.

Interested in 8th Light's services? Let's talk.

Contact Us