使用OCMock版本2.1.1testing类的方法
我正在尝试检查是否使用OCMock调用类方法。 我从OCMock网站和其他答案收集到的新的OCMock版本(2.1)增加了对桩类方法的支持。
我正在尝试做同样的事情:
DetailViewController:
+(BOOL)getBoolVal { return YES; }
testing用例:
-(void) testClassMethod { id detailMock = [OCMockObject mockForClass:[DetailViewController class]]; [[[detailMock stub] andReturnValue:OCMOCK_VALUE((BOOL){YES})] getBoolVal:nil]; }
testing正在运行,并且也成功,但即使从DetailViewController
getBoolVal
方法返回NO而不是YES,它也会成功。 在保持该方法的断点时,testing执行不会停止指示该方法未被调用。
那么我如何检查一个类的方法呢?
编辑
只是更仔细地观察了最新版本的OCMock,我认为你在解释你正在testing的类方法的方法是正确的,所以只是你用错误的签名来调用它的问题?
下面的答案是一般情况(当一个被测方法调用类方法时也是如此)。
原版的
使用OCMock检查类方法有点棘手。 你现在正在做的是创build一个叫做detailMock的模拟对象,并存储一个名为getBoolVal的实例方法:(顺便说一下,你的方法原型不会接受一个参数,所以你不应该通过零来得到真正的挑剔,如果你想遵循苹果的指导方针,他们build议不要在getter中使用“get”一词(除非你发送一个指针引用来获取set))。 编译不会失败,因为detailMock是一个id,并愿意响应任何select器。
那么如何testing一个Class方法? 一般情况下,你需要做一些调整。 这是我怎么做的。
让我们看看我们如何伪造NSURLConnection
,你也应该能够应用到你的课堂上。
从扩展你的课程开始:
@interface FakeNSURLConnection : NSURLConnection + (id)sharedInstance; + (void)setSharedInstance:(id)sharedInstance; + (void)enableMock:(id)mock; + (void)disableMock; - (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate; @end
请注意,我对testingconnectionWithRequest:delegate感兴趣,并且已经扩展该类以添加与类方法具有相同签名的公共实例方法。 我们来看看实现:
@implementation FakeNSURLConnection SHARED_INSTANCE_IMPL(FakeNSURLConnection); SWAP_METHODS_IMPL(NSURLConnection, FakeNSURLConnection); DISABLE_MOCK_IMPL(FakeNSURLConnection); ENABLE_MOCK_IMPL(FakeNSURLConnection); + (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
那么这里发生了什么? 首先有一些macros,我将在下面讨论。 接下来,我已经重写了类方法来调用实例方法。 我们可以使用OCMock
来模拟实例方法,所以通过类方法调用实例方法,我们可以让类方法调用模拟。
尽pipe我们不想在我们的真实代码中使用FakeNSURLConnection,但我们确实希望在我们的testing中使用它。 我们应该怎么做? 我们可以调用NSURLConnection
和FakeNSURLConnection
之间的类方法。 这意味着,我们调用NSURLConnection connectionWithRequest:delegate
调用FakeNSURLConnection connectionWithRequest:delegate
。 这让我们看到了我们的macros观:
#define SWAP_METHODS_IMPL(REAL, FAKE) \ + (void)swapMethods \ { \ Method original, mock; \ unsigned int count; \ Method *methodList = class_copyMethodList(object_getClass(REAL.class), &count); \ for (int i = 0; i < count; i++) \ { \ original = class_getClassMethod(REAL.class, method_getName(methodList[i])); \ mock = class_getClassMethod(FAKE.class, method_getName(methodList[i])); \ method_exchangeImplementations(original, mock); \ } \ free(methodList); \ } #define DISABLE_MOCK_IMPL(FAKE) \ + (void)disableMock \ { \ if (_mockEnabled) \ { \ [FAKE swapMethods]; \ _mockEnabled = NO; \ } \ } #define ENABLE_MOCK_IMPL(FAKE) \ static BOOL _mockEnabled = NO; \ + (void)enableMock:(id)mockObject; \ { \ if (!_mockEnabled) \ { \ [FAKE setSharedInstance:mockObject]; \ [FAKE swapMethods]; \ _mockEnabled = YES; \ } \ else \ { \ [FAKE disableMock]; \ [FAKE enableMock:mockObject]; \ } \ } #define SHARED_INSTANCE_IMPL() \ + (id)sharedInstance \ { \ return _sharedInstance; \ } #define SET_SHARED_INSTANCE_IMPL() \ + (void)setSharedInstance:(id)sharedInstance \ { \ _sharedInstance = sharedInstance; \ }
我build议这样的事情,所以你不会不小心重新调整你的课堂方法。 那么你将如何使用这个?
id urlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class]; [FakeNSURLConnection enableMock:urlConnectionMock]; [_mocksToDisable addObject:FakeNSURLConnection.class]; [[[urlConnectionMock expect] andReturn:urlConnectionMock] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];
这几乎是 – 你已经调整了方法,所以你的假类将被召唤,这将叫你的模拟。
啊,但最后一件事。 _mocksToDisable是一个NSMutableArray
,它将包含我们调用的每个类的类对象。
- (void)tearDown { for (id mockToDisable in _mocksToDisable) { [mockToDisable disableMock]; } }
我们在tearDown
做了这个操作,以确保在testing运行后我们的课程没有问题 – 不要在testing中正确使用它,因为如果有一个例外,那么不是所有的testing代码都可以被执行,但是tearDown总是会被执行。
也许有其他的模拟技术可以使这个更简单,但是我发现它并没有那么糟糕,因为你只写了一次,可以多次使用它。
也许我在这里错过了一些东西(我知道这+(BOOL)getBoolVal { return YES; }
),但是你的类签名是+(BOOL)getBoolVal { return YES; }
+(BOOL)getBoolVal { return YES; }
在你的testing中,你正在调用getBoolVal:nil
那些不匹配,对吧? 在这种情况下,你的阶级模拟会说:“噢,这不是我所期望的签名”,我相信,试着把它传递给解职class。 请参阅OCMock源代码中的forwardInvocationForClassObject
。
至于为什么你会得到NO(因为底层类也返回YES,这使得这种testingtypes没有意义,但这是另一个问题),我不是100%确定,但也许它只是一个C – 主义 – 'indeterminate value,void,0(false / NO)“
- 自定义地图路径线
- 新的iOS应用程序与Facebook Graph api v2.0挣扎
- 如何获得与谷歌驱动器的iOS应用程序中的video和audio文件
- iOS – 获取从照片卷轴中select的图像的文件大小
- 如何在Swift中自定义三元运算符
- UITextField委托跳转到100%的CPU使用率和使用键盘快捷键时崩溃
- 在UIAlertController之外轻触如何解除UIAlertController?
- 从API的viewDidLoad,viewWillAppear或viewDidAppear加载数据是更好的地方?
- Swift 3项目中的Unity实现:应用程序启动崩溃 – MetadataCache :: Initialize()