Objective c 如何在Kiwi中使用存根方法块?

Objective c 如何在Kiwi中使用存根方法块?,objective-c,unit-testing,mocking,tdd,kiwi,Objective C,Unit Testing,Mocking,Tdd,Kiwi,我想存根一个方法,它使用Kiwi将一个块作为参数。以下是代码的完整解释: 我有一个名为TestedClass的类,它有一个方法testedMethod,该方法依赖于类NetworkClass,该类通过AFNetworking调用服务器,并通过block返回其响应。翻译成代码: @interface TestedClass : NSObject -(void)testMethod; @end -(void)testMethod { NetworkClass *networkCla

我想存根一个方法,它使用Kiwi将一个块作为参数。以下是代码的完整解释:

我有一个名为
TestedClass
的类,它有一个方法
testedMethod
,该方法依赖于类
NetworkClass
,该类通过AFNetworking调用服务器,并通过block返回其响应。翻译成代码:

@interface TestedClass : NSObject
    -(void)testMethod;
@end

-(void)testMethod
{
    NetworkClass *networkClass = [[NetworkClass alloc] init];

    [networkClass networkMethod:^(id result)
    {
        // code that I want to test according to the block given which I want to stub
        ...
    }];
}



typedef void (^NetworkClassCallback)(id result);

 @interface NetworkClass : NSObject
 -(void)networkMethod:(NetworkClassCallback)handler;
 @end

-(void) networkMethod:(NetworkClassCallback)handler
{
    NSDictionary *params = @{@"param":@", @"value"};
    NSString *requestURL = [NSString stringWithFormat:@"www.someserver.com"];
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURLURLWithString:requestURL]];
    NSURLRequest *request = [httpClient requestWithMethod:@"GET" path:requestURL parameters:params];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
         handler(responseObject);
    }

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    handler(nil);
}];

[operation start];
}
如何使用Kiwi将
networkMethod
与block一起存根以进行单元测试
testMethod


更新:发现如何在猕猴桃做这件事,请参阅下面我的答案

以下是您在新西兰的做法:

首先,您必须将依赖项注入
NetworkClass
TestedClass
(如果不清楚如何注入,请添加注释,我将进行解释;为了简单起见,这可以作为属性来完成。这样您就可以对
NetworkClass
的模拟对象进行操作)

然后,为网络类创建模拟,并创建要进行单元测试的类:

SPEC_BEGIN(TestSpec)

describe(@"describe goes here", ^{
    it(@"should test block", ^{
        NetworkClass *mockNetworkClass = [NetworkClass mock];
        KWCaptureSpy *spy = [mockNetworkClass captureArgument:@selector(networkMethod:) atIndex:0];
        TestedClass testClass = [TestedClass alloc] init];
        testClass.networkClass = mockNetworkClass;
        [testClass testMethod];

        NetworkClassCallback blockToRun = spy.argument;
        blockToRun(nil);

        // add expectations here

    });
});

SPEC_END
要解释这里发生了什么:

您正在创建
TestedClass
并调用
testMethod
。然而,在此之前,我们正在创建一个名为
Spy
——它的任务是在调用
networkMethod:
时捕获第一个参数中的块。现在,是时候实际执行块本身了

这里很容易混淆,所以我要强调这一点:调用的顺序很重要;首先声明spy,然后调用被测试的方法,然后才真正调用并执行块

这将使您能够在执行块时检查所需内容


希望对其他人有所帮助,因为我花了很长时间才理解这个流程。

如果您不想公开
TestedClass
NetworkClass
实例,该怎么办?(它是
只读的
或私有财产)?这是可能的吗?需要依赖注入才能使这种规范正常工作吗?这个模式听起来很有趣,但我现在不想花时间。另外,对*spy的赋值是否应该调用[mockNetworkClass captureArgument:…(而不是networkClass)还是我遗漏了什么?@Byofuel是的,你必须提供模拟对象-这是通过赋值完成的:testClass.networkClass=mockNetworkClass,谢谢你的输入错误;)@AdamWaite很抱歉迟才回复!在你的规范文件中,你可以添加testedClass的一个类别,并且只在那里公开属性-这样你就提供了封装在能够注入该属性时需要。@Byofuel FTI,我刚刚尝试在真实对象上调用
captureArgument
,但它确实执行了块,但是,我不会这样做,因为真实实现可能也会调用该块,这可能会产生副作用。