首页 文章

如何在OCMock中存根一个类方法?

提问于
浏览
36

我经常在我的iPhone Objective-C单元测试中发现我想要一个类方法,例如NSUrlConnection的sendSynchronousRequest:returningResponse:error:方法 .

简化示例:

- (void)testClassMock
{
    id mock = [OCMockObject mockForClass:[NSURLConnection class]];
    [[[mock stub] andReturn:nil] sendSynchronousRequest:nil returningResponse:nil error:nil];
}

运行时,我得到:

Test Case '-[WorklistTest testClassMock]' started.
Unknown.m:0: error: -[WorklistTest testClassMock] : *** -[NSProxy doesNotRecognizeSelector:sendSynchronousRequest:returningResponse:error:] called!
Test Case '-[WorklistTest testClassMock]' failed (0.000 seconds).

我很难找到关于此的任何文档,但我认为OCMock不支持类方法 .

经过很多谷歌搜索,我发现了这个提示 . 它有效,但非常麻烦:http://thom.org.uk/2009/05/09/mocking-class-methods-in-objective-c/

无论如何在OCMock中这样做?或者有人会想到一个聪明的OCMock类别对象可以编写来完成这种事情吗?

5 回答

  • 6

    来自Ruby的世界,我完全理解你想要完成的事情 . 显然,你真的提前三个小时试图做同样的事情(时区的事情?:-) .

    无论如何,我认为在OCMock中不需要支持这种方式,因为无论何时,何地或谁调用该方法,存根类方法都需要逐字地进入类并更改其方法实现 . 这与OCMock似乎所做的相反,它是为您提供一个代理对象,您可以直接操作该代理对象并代替指定类的"real"对象 .

    例如,想要存根NSURLConnection sendSynchronousRequest:returningResponse:error:方法似乎是合理的 . 但是,通常在我们的代码中使用这个调用有点埋没,因此使它参数化并在NSURLConnection类的模拟对象中交换非常尴尬 .

    出于这个原因,我认为你非常麻烦的方法看起来非常麻烦 - 我们怎么认同这个问题是一个非常简洁的解决方案 . -1682423_

  • 17

    Update for OCMock 3

    OCMock已经对其语法进行了现代化,以支持类方法存根:

    id classMock = OCMClassMock([SomeClass class]);
    OCMStub(ClassMethod([classMock aMethod])).andReturn(aValue);
    

    Update

    OCMock现在支持开箱即用的类方法存根 . OP的代码现在应该按照发布的方式工作 . 如果存在与类方法同名的实例方法,则语法为:

    [[[[mock stub] classMethod] andReturn:aValue] aMethod]
    

    OCMock's Features .

    Original Answer

    Barry Wark回答后的示例代码 .

    假类,只是存根connectionWithRequest:委托:

    @interface FakeNSURLConnection : NSURLConnection
    + (id)sharedInstance;
    + (void)setSharedInstance:(id)sharedInstance;
    + (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
    - (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
    @end
    @implementation FakeNSURLConnection
    static id _sharedInstance;
    + (id)sharedInstance { if (!_sharedInstance) { _sharedInstance = [self init]; } return _sharedInstance; }
    + (void)setSharedInstance:(id)sharedInstance { _sharedInstance = sharedInstance; }
    + (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate {
        return [FakeNSURLConnection.sharedInstance connectionWithRequest:request delegate:delegate];
    }
    - (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate { return nil; }
    @end
    

    切换到模拟:

    {
        ...
        // Create the mock and swap it in
        id nsurlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class];
        [FakeNSURLConnection setSharedInstance:nsurlConnectionMock];
        Method urlOriginalMethod = class_getClassMethod(NSURLConnection.class, @selector(connectionWithRequest:delegate:));
        Method urlNewMethod = class_getClassMethod(FakeNSURLConnection.class, @selector(connectionWithRequest:delegate:));
        method_exchangeImplementations(urlOriginalMethod, urlNewMethod);
    
        [[nsurlConnectionMock expect] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];
    
        ...
        // Make the call which will do the connectionWithRequest:delegate call
        ...
    
        // Verify
        [nsurlConnectionMock verify];
    
        // Unmock
        method_exchangeImplementations(urlNewMethod, urlOriginalMethod);
    }
    
  • 4

    这是一个很好的'gist',为类方法提供了一个swizzle实现:https://gist.github.com/314009

  • 2

    如果你修改你的测试方法以获取一个注入 NSURLConnection 类的参数,那么's relatively easy to pass in a mock that responds to the given selector (you may have to create a dummy class in your test module which has the selector as an instance method and mock that class). Without this injection, you'使用类方法,基本上使用 NSURLConnection (类)作为单例,因此已经落入使用单例的反模式对象和代码的可测试性受到影响 .

  • 45

    链接到问题中的博客帖子和RefuX gist激励我提出阻止他们的想法实现:https://gist.github.com/1038034

相关问题