首页 > 解决方案 > 有没有办法在 XCTest 函数中模拟用户的输入?

问题描述

我有期望用户输入的方法

@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

然后我想通过一个测试用例来介绍这个方法

@interface TeamFormationTests : XCTestCase

@end

@implementation TeamFormationTests

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

    // emulate user's input here
}

@end

那么,如何在测试用例功能中模拟用户的输入呢?

标签: objective-cunit-testinguser-inputxctestnsfilehandle

解决方案


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

推荐阅读