0

我正在关注测试驱动的 iOS 开发中的示例,在一种情况下,有一个单元测试可确保委托获得错误方法的“简化”版本。因此,这里不涉及太多细节,而是相关对象:

  • 通信器:负责进行网络调用的对象
  • manager:指示通信器进行调用,然后将结果推送给其委托。
  • 委托:符合StackOverflowManagerDelegate协议的经理委托。得到结果并处理它。

所以这就是测试:

@implementation QuestionCreationTests

{
    @private
    StackOverflowManager *mgr;
}
- (void)testErrorReturnedToDelegateIsNotErrorNotifiedByCommunicator {
    MockStackOverflowManagerDelegate *delegate =
    [[MockStackOverflowManagerDelegate alloc] init];
    mgr.delegate = delegate;
    NSError *underlyingError = [NSError errorWithDomain: @"Test domain"
                                                   code: 0 userInfo: nil];
    [mgr searchingForQuestionsFailedWithError: underlyingError];
    XCTAssertFalse(underlyingError == [delegate fetchError],
                  @"Error should be at the correct level of abstraction");
}

这是searchingForQuestionsFailedWithErrorStackOverflowManager 中的实现,其中管理器简单地将通信器返回的原始错误简化并将简化的版本发送给委托。

- (void)searchingForQuestionsFailedWithError:(NSError *)error {
    NSDictionary *errorInfo = [NSDictionary dictionaryWithObject: error
                                                          forKey: NSUnderlyingErrorKey];
    NSError *reportableError = [NSError
                                errorWithDomain: StackOverflowManagerSearchFailedError
                                code: StackOverflowManagerErrorQuestionSearchCode
                                userInfo:errorInfo];
    [delegate fetchingQuestionsOnTopic: nil
                       failedWithError: reportableError];
}

作者建议为此工作..我们实际上必须为经理委托创建一个模拟对象,如下所示:

@interface MockStackOverflowManagerDelegate : NSObject <StackOverflowManagerDelegate>
@property (strong) NSError *fetchError;

@end

@implementation MockStackOverflowManagerDelegate

@synthesize fetchError;

- (void)fetchingQuestionsOnTopic: (Topic *)topic
                 failedWithError: (NSError *)error {
    self.fetchError = error;
}

@end

这是声明StackOverflowManagerDelegate

@protocol StackOverflowManagerDelegate <NSObject>    
- (void)fetchingQuestionsOnTopic: (Topic *)topic
                 failedWithError: (NSError *)error {    
@end

问题:我一直在浏览本书的所有示例,并尝试使用 OCMock 而不是作者正在做的手动制作的示例。(我只是认为这会少很多时间)。到目前为止一切正常..但我被困在这里..我如何伪造一个名为fetchError委托的属性?这就是我现在所拥有的:

- (void)testErrorReturnedToDelegateIsNotErrorNotifiedByCommunicator {
    id <StackOverflowManagerDelegate> delegate = 
       [OCMockObject mockForProtocol:@protocol(StackOverflowManagerDelegate)];
    mgr.delegate = delegate;

    NSError *underlyingError = [NSError errorWithDomain: @"Test domain"
                                                   code: 0 userInfo: nil];

    [mgr searchingForQuestionsFailedWithError: underlyingError];

    // compiler error here: no known instance method for selector 'fetchError'
    XCTAssertFalse(underlyingError == [mgr.delegate fetchError], @"error ");
}

在经理的胆量中,经理调用fetchingQuestionsOnTopic了代表..我知道我可以通过使用 [[[delegate stub] andCall:@selector(differentMethod:) onObject:differentObject] fetchingQuestionsOnTopic:[OCMArg any]]where differentMethodwould do what I want it do..我只是不知道如何处理以下结果differentMethod:我不知道不知道如何将其存储在委托的模拟属性中。


更新:作为以下答案的后续..这是单元测试的实现,可确保委托仍然可以使用底层错误:

- (void)testErrorReturnedToDelegateDocumentsUnderlyingError {
    MockStackOverflowManagerDelegate *delegate =
    [[MockStackOverflowManagerDelegate alloc] init];
    mgr.delegate = delegate;
    NSError *underlyingError = [NSError errorWithDomain: @"Test domain"
                                                   code: 0 userInfo: nil];
    [mgr searchingForQuestionsFailedWithError: underlyingError];
    XCTAssertEqual([[[delegate fetchError] userInfo]
                          objectForKey: NSUnderlyingErrorKey], underlyingError,
                         @"The underlying error should be available to client code");
}

这是它的 OCMock 版本:

- (void)testErrorReturnedToDelegateDocumentsUnderlyingErrorOCMock {
    id delegate =
    [OCMockObject mockForProtocol:@protocol(StackOverflowManagerDelegate)];
    mgr.delegate = delegate;

    NSError *underlyingError = [NSError errorWithDomain: @"Test domain"
                                                   code: 0 userInfo: nil];

    [[delegate expect] fetchingQuestionsFailedWithError:
       [OCMArg checkWithBlock:^BOOL(id param) {

        return ([[param userInfo] objectForKey:NSUnderlyingErrorKey] == underlyingError);

    }]];

    [mgr searchingForQuestionsFailedWithError: underlyingError];

    [delegate verify];
}
4

1 回答 1

1

试试这个:

- (void)testErrorReturnedToDelegateIsNotErrorNotifiedByCommunicator {
    id delegate = 
       [OCMockObject mockForProtocol:@protocol(StackOverflowManagerDelegate)];
    mgr.delegate = delegate;

    NSError *underlyingError = [NSError errorWithDomain: @"Test domain"
                                                   code: 0 userInfo: nil];

    [[delegate expect] fetchingQuestionsOnTopic:OCMOCK_ANY 
                                failedWithError:[OCMArg isNotEqual:underlyingError]];

    [mgr searchingForQuestionsFailedWithError:underlyingError];

    [delegate verify];
}

要测试经理报告的错误的域、代码和用户信息(如果我没记错的话,那是那本书中的另一个测试用例——我很久以前读过),你可以这样做:

    id <StackOverflowManagerDelegate> delegate = 
       [OCMockObject mockForProtocol:@protocol(StackOverflowManagerDelegate)];
    mgr.delegate = delegate;

    NSError *underlyingError = [NSError errorWithDomain: @"Test domain"
                                                   code: 0 userInfo: nil];

    NSError *expectedError = [NSError errorWithDomain:@"expected domain" 
                                                 code:0/* expected code here*/ 
                                             userInfo:@{NSUnderlyingErrorKey: underlyingError}];
    [[delegate expect] fetchingQuestionsOnTopic:OCMOCK_ANY 
                                failedWithError:expectedError];

    [mgr searchingForQuestionsFailedWithError: underlyingError];

    [delegate verify];
于 2013-10-06T20:40:54.717 回答