首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OCMock和块测试,执行

OCMock和块测试,执行
EN

Stack Overflow用户
提问于 2013-07-15 07:20:02
回答 3查看 6.7K关注 0票数 11

下面是测试中的方法:

代码语言:javascript
复制
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {

    NSDictionary *userPassD = @{@"user":userName,
                                @"pass":pass};
    [_loginCntrl loginWithUserPass:userPassD withSuccess:^(NSString *authToken){
        // save authToken to credential store
    } failure:^(NSString *errorMessage) {
        // alert user pass was wrong
    }];    
}

我想测试的是,在那个成功块中,使用适当的方法调用了另一个依赖项/OCMockObject _credStore。因此,目前loginCtrl和credStore依赖项是OCMockObjects,我可以对它们进行存根/期望。

我是否会在调用时使用存根loginController来以某种方式执行该块?我已经看过一些关于使用OCMock的存根块的问题,我不能理解他们在做什么,以及它是否适合这种情况。

实际上,我想要做的就是使用OCMock来触发这个块(成功调用??)这样就可以在_credStore上验证_credStore saveUserPass的代码了。

我停下来的地方是:

代码语言:javascript
复制
- (void)test_loginWithuserPass_succeeds_should_call_credStore_setAuthToken {

    NSDictionary *userPassD = @{@"user":@"mark",
                                @"pass":@"test"};
    id successBlock = ^ {
        // ??? isn't this done in the SUT?
    };

    [[[_loginController stub] andDo:successBlock] loginWithUserPass:userPassD withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
    [[_credentialStore expect] setAuthToken:@"passed back value from block"];
    [_docServiceSUT loginWithUser:@"mark" andPass:@"test"];
    [_credentialStore verify];
}

ETA:这是我基于Ben的例子所做的,但不能工作,得到了一个EXC_BAD_ACCESS异常:

代码语言:javascript
复制
// OCUnit test method
- (void)test_loginWithUserPass_success_block_should_call_credentials_setAuthToken {

    void (^proxyBlock)(NSInvocation*) = ^(NSInvocation *invocation) {
        void(^successBlock)(NSString *authToken);
        [invocation getArgument:&successBlock atIndex:3]; // should be 3 because my block is the second param
        successBlock(@"myAuthToken");
    };

    [[[_loginController expect] andDo:proxyBlock] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
    [[_credentialStore expect] setAuthToken:@"myAuthToken"];
    [_docServiceSUT loginWithUser:@"mark" andPass:@"myPass"];
    [_loginController verify];
    [_credentialStore verify];
}

//method under test
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {

    NSDictionary *userPassD = @{@"user":userName,
                                @"pass":pass};

    void(^onSuccess)(NSString *) = ^(NSString *authToken){

        [SVProgressHUD dismiss];
        [_credentials setAuthToken:authToken];

        // Ask user to enter the 6 digit authenticator key
        [self askUserForAuthenticatorKey];
    };

    void(^onFailure)(NSString *) = ^(NSString *errorMessage) {

        [SVProgressHUD dismiss];
        [_alertSender sendAlertWithMessage:errorMessage andTitle:@"Login failed"];
    };

    [SVProgressHUD show];
    [_loginCntrl loginWithUserPass:userPassD withSuccess:onSuccess
      failure:onFailure];
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-07-16 01:28:37

如果我没看错,这可能会做你想做的事情:

代码语言:javascript
复制
@interface ExampleLC : NSObject
- (void)loginWithUserPass:userPassD withSuccess:(void (^)(NSString *authToken))successBlock failure:(void (^)(NSString *errorMessage))failureBlock;
@end
@implementation ExampleLC
- (void)loginWithUserPass:userPassD withSuccess:(void (^)(NSString *authToken))successBlock failure:(void (^)(NSString *errorMessage))failureBlock
{
}
@end
@interface Example : NSObject {
    @public
    ExampleLC *_loginCntrl;
}
- (void)saveToken:(NSString *)authToken;
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass;
@end
@implementation Example
- (void)saveToken:(NSString *)authToken
{
}
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {

    NSDictionary *userPassD = @{@"user":userName,
                                @"pass":pass};
    [_loginCntrl loginWithUserPass:userPassD withSuccess:^(NSString *authToken){
        // save authToken to credential store
        [self saveToken:authToken];
    } failure:^(NSString *errorMessage) {
        // alert user pass was wrong
    }];
}
@end


@interface loginTest : SenTestCase

@end

@implementation loginTest

- (void)testExample
{
    Example *exampleOrig = [[Example alloc] init];
    id loginCntrl = [OCMockObject mockForClass:[ExampleLC class]];
    [[[loginCntrl expect] andDo:^(NSInvocation *invocation) {
        void (^successBlock)(NSString *authToken) = [invocation getArgumentAtIndexAsObject:3];
        successBlock(@"Dummy");
    }] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
    exampleOrig->_loginCntrl = loginCntrl;
    id example = [OCMockObject partialMockForObject:exampleOrig];
    [[example expect] saveToken:@"Dummy"];
    [example loginWithUser:@"ABC" andPass:@"DEF"];
    [loginCntrl verify];
    [example verify];
}
@end

此代码允许使用您指定的参数强制调用真正的成功块,然后您可以对其进行验证。

票数 13
EN

Stack Overflow用户

发布于 2013-10-06 12:01:16

我正在使用的代码在很大程度上是基于块的,所以我对你的问题非常熟悉。

换句话说:

代码语言:javascript
复制
- (ReturnObject *)methodThatWeWantToTest:(Foobar *)bar
{    
       [self.somethingElse doSomethingAndCallBlock:^{
         // really want to test this code
       } secondBock:^{
        // even more code to test
       }];
}

为了解决您在这里提出的完全相同的问题,我们创建了单元测试助手类,该类的方法与调用我们需要测试的块的方法具有相同的签名。

对于您的代码示例,它是一个模拟方法,不返回任何内容,接受一个id参数和两个块。

下面是您需要的模拟方法的示例和利用

代码语言:javascript
复制
- (void)mockSuccessBlockWithOneArgumentTwoBlocks:(id)firstArgument
                                    successBlock:(void (^)(NSString *authtoken))successBlock
                                    failureBlock:(void (^)(NSString *errorMessage))failureBlock    
{
    successBlock(@"mocked unit test auth token");
}

- (void)testLoginWithUserCallsSomethingInCredStoreOnLoginSuccess
{
    LoginService *loginService = [[LoginService alloc] init];

    // init mocks we need for this test
    id credStoreMock = [OCMockObject niceMockForClass:[MyCredStore class]];
    id loginCtrlMock = [OCMockObject niceMockForClass:[MyLoginCtrl class]];

    // force login controller to call success block when called with loginWithUserPass
    // onObject:self - if mock method is in the same unit test, use self. if it is helper object, point to the helper object.
    [[[loginCtrlMock stub] andCall:@selector(mockSuccessBlockWithOneArgumentTwoBlocks:successBlock:failureBlock::) onObject:self] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];

    // setup mocks
    loginService.credStore = credStoreMock;
    loginService.loginCtrl = loginCtrlMock;

    // expect/run/verify
    [[credStore expect] callSomethingFromSuccessBlock];
    [loginService loginWithUser:@"testUser" andPass:@"testPass"];
    [credStore verify];
}

希望它能帮上忙!如果你有任何问题,请告诉我,我们已经让它工作了。

票数 7
EN

Stack Overflow用户

发布于 2013-07-15 10:26:54

我觉得你可以和间谍一起做这件事。阅读此wiki page on spies,看起来您可以捕获传入的块,并在测试中自己调用它。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17644772

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档