0

I have method which expects user's input

@implementation TeamFormation
- (void)run {
    NSFileHandle *kbd = [NSFileHandle fileHandleWithStandardInput];
    NSData *inputData = [kbd availableData];
    NSString *option = [[[NSString alloc] initWithData:inputData
                     encoding:NSUTF8StringEncoding] substringToIndex:1];
    NSLog(@"%@",option);
}
@end

Then I would like to cover this method by a test case

@interface TeamFormationTests : XCTestCase

@end

@implementation TeamFormationTests

- (void)testTeamFormation {
    TeamFormation *teamFormation = [TeamFormation new];
    [teamFormation run];

    // emulate user's input here
}

@end

So, how to emulate user's input in test case function?

Ihar Katkavets
  • 1,510
  • 14
  • 25

1 Answers1

2

You have many options how to achieve this. Two obvious below.

Change run to accept an argument

  • - (void)run to - (void)runWithFileHandle:(NSFileHandle *)handle
  • your app code can pass stdin filehandle
  • your test code can pass handle to a file with desired input

Mock it with protocol

Create DataProvider protocol:

@protocol DataProvider

@property(readonly, copy) NSData *availableData;

@end

Make NSFileHandle to conform to this protocol:

@interface NSFileHandle (AvailableDataProvider) <DataProvider>
@end

Store an object implementing this protocol on TeamFormation:

@interface TeamFormation : NSObject

@property (nonatomic, nonnull, strong) id<DataProvider> dataProvider;

- (NSString *)run;

@end

By default, use stdin file handle:

@implementation TeamFormation

- (instancetype)init {
    if ((self = [super init]) == nil) {
        return nil;
    }
    
    _dataProvider = [NSFileHandle fileHandleWithStandardInput];
    return self;
}

- (NSString *)run {
    NSData *inputData = [self.dataProvider availableData];
    return [[[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding] substringToIndex:1];
}

@end

Create TestDataProvider in your test:

@interface TestDataProvider: NSObject<DataProvider>

@property (nonatomic, strong, nonnull) NSData *dataToProvide;

@end

@implementation TestDataProvider

- (instancetype)init {
    if ((self = [super init]) == nil) {
        return nil;
    }
    
    _dataToProvide = [NSData new];
    
    return self;
}

- (NSData *)availableData {
    return _dataToProvide;
}

@end

And use it in TestFormationTests:

@implementation TeamFormationTests

- (void)testFormationRun {
    TestDataProvider *dataProvider = [TestDataProvider new];
    TeamFormation *formation = [TeamFormation new];
    formation.dataProvider = dataProvider;
    
    XCTAssertThrows([formation run]);
    
    dataProvider.dataToProvide = [@"foo" dataUsingEncoding:NSUTF8StringEncoding];
    XCTAssertEqualObjects([formation run], @"f");
    
    dataProvider.dataToProvide = [@"bar" dataUsingEncoding:NSUTF8StringEncoding];
    XCTAssertEqualObjects([formation run], @"b");
}

@end
zrzka
  • 20,249
  • 5
  • 47
  • 73