iosobjective-cintegration-testingkif-frameworkkif

Possible to build a scenario outline with multiple examples with KIF?


I'm using KIF for integration/acceptance testing in my iOS app and I have an example that needs to run through ~50 static table rows expecting specific content on the view pushed onto the stack.

If I was in the Cucumber/Rspec world I would write a Scenario Outline similar to Cucumber's example:

Scenario Outline: eating
  Given there are <start> cucumbers
  When I eat <eat> cucumbers
  Then I should have <left> cucumbers

 Examples:
   | start | eat | left |
   |  12   |  5  |  7   |
   |  20   |  5  |  15  |

Where the scenario would run for each example and record individual pass/failures. Is there an easy way recreate this with KIF (2.0)? I could almost recreate it by looping through each 'example' and reporting a failure if one execution of the loop fails, but I'm concerned that would only appear as one failure when in reality multiple examples were tested.


Solution

  • I realized after asking the question that what I'd really need to use to achieve this is SenTestingKit (not necessarily KIF). I used a variation on the solution from http://briancoyner.github.io/blog/2011/09/12/ocunit-parameterized-test-case/ to run multiple tests using the same test method and different values each run.

    I added a description method to my SenTestCase subclass, which provides support for Xcode 5's test navigator. With one test method, you can see a run of the test for each parameter set. By modifying the description method you can uniquely name each run. The approach is similar to the blog post I linked to but I'll provide my implementation here:

    WMScenarioOutline.h

    #import <KIF/KIF.h>
    
    @interface WMScenarioOutline : SenTestCase
    
    @property (nonatomic, copy) NSDictionary *example;
    
    - (id)initWithInvocation:(NSInvocation *)anInvocation example:(NSDictionary *)example;
    
    @end
    

    WMScenarioOutline.m

    #import "WMScenarioOutline.h"
    
    @implementation WMScenarioOutline
    
    + (id)defaultTestSuite
    {
        SenTestSuite *testSuite = [[SenTestSuite alloc] initWithName:NSStringFromClass(self)];
        [self addTestCaseWithExample:@{@"name" : @"Foo", @"value" : @0} toTestSuite:testSuite];
        [self addTestCaseWithExample:@{@"name" : @"Bar", @"value" : @1} toTestSuite:testSuite];
    
        return testSuite;
    }
    
    + (void)addTestCaseWithExample:(NSDictionary *)example toTestSuite:(SenTestSuite *)suite
    {
        NSArray *testInvocations = [self testInvocations];
        for (NSInvocation *testInvocation in testInvocations) {
            SenTestCase *testCase = [[WMScenarioOutline alloc] initWithInvocation:testInvocation example:example];
            [suite addTest:testCase];
        }
    }
    
    - (id)initWithInvocation:(NSInvocation *)anInvocation example:(NSDictionary *)example
    {
        self = [super initWithInvocation:anInvocation];
        if (self) {
            _example = example;
        }
    
        return self;
    }
    
    - (void)testScenarioOutline
    {
        [tester runBlock:^KIFTestStepResult(NSError *__autoreleasing *error) {
            NSLog(@"Testing example: %@", self.example);
            return [self.example[@"value"] intValue] == 0 ? KIFTestStepResultSuccess : KIFTestStepResultFailure;
        }];
    }
    
    - (NSString *)description
    {
        NSInvocation *invocation = [self invocation];
    
        NSString *name = NSStringFromSelector(invocation.selector);
        if ([name hasPrefix:@"test"]) {
            return [NSString stringWithFormat:@"-[%@ %@%@]", NSStringFromClass([self class]), NSStringFromSelector(invocation.selector), self.example[@"name"]];
        }
        else {
            return [super description];
        }
    }
    
    @end
    

    Unique runs of the same test method with different parameters

    There is plenty of room here for improvement but this is a good place to start.