EDIT
Просто более внимательно посмотрел на последнюю версию OCMock, и я думаю, что вы правы в интерпретации способ поиздеваться метод класса, который вы непосредственно тестирование, так что это вопрос просто, что вы называете это неправильной подписью?
Ответ ниже - общий случай (так что также полезно, когда тестируемый метод вызывает метод класса).
Оригинал
Проверка метода класса с OCMock немного сложнее.
То, что вы сейчас делаете, это создание объекта
, называемого detailMock, и обрезание
экземпляра метод getBoolVal:
(Кстати, ваш прототип метода не принимает аргумента, поэтому вы не должны пропускать нуль это - чтобы получить действительно nit-picky, если вы хотите следовать рекомендациям Apple, они не рекомендуют использовать слово get в getter (если вы не отправляете ссылку на указатель, чтобы получить set)). Компиляция не прерывается, потому что detailMock - это идентификатор и готов ответить на любой селектор.
Итак, как протестировать метод класса? В общем случае вам нужно будет немного подпрыгнуть. Вот как я это делаю.
Давайте посмотрим, как мы будем подделывать NSURLConnection
, которые вы также сможете применить к своему классу.
Start, расширяя свой класс:
@interface FakeNSURLConnection : NSURLConnection
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
+ (void)enableMock:(id)mock;
+ (void)disableMock;
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
@end
Заметьте, что я заинтересован в тестировании connectionWithRequest: делегат и что я расширил класс для добавления общедоступного экземпляра метода с той же сигнатурой, что и класса. Давайте посмотрим на реализацию:
@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
Так что же здесь происходит? Сначала есть некоторые макросы, о которых я расскажу ниже. Затем я переопределил метод класса, чтобы вызвать метод экземпляра. Мы можем использовать OCMock
для моделирования методов экземпляра, поэтому, если метод класса вызовет метод экземпляра, мы можем вызвать метод класса, называемый mock.
Мы не хотим использовать FakeNSURLConnection в нашем реальном коде, но мы хотим использовать его в нашем тестировании. Как мы можем это сделать? Мы можем swizzle методы класса между NSURLConnection
и FakeNSURLConnection
. Это означает, что после того, как мы подпишем звонок NSURLConnection connectionWithRequest:delegate
с вызовом FakeNSURLConnection connectionWithRequest:delegate
. Это подводит нас к нашим макрокомандам:
#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; \
}
Я бы рекомендовал что-то вроде этого, так что вы не случайно повторно Swizzle ваших методов класса. Итак, как бы вы это использовали?
id urlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class];
[FakeNSURLConnection enableMock:urlConnectionMock];
[_mocksToDisable addObject:FakeNSURLConnection.class];
[[[urlConnectionMock expect] andReturn:urlConnectionMock] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];
Это довольно много, это - вы swizzled методы, чтобы ваш фальшивый класс будет вызвана, и что будет вызывать издеваться.
Ах, но последнее. _mocksToDisable - это NSMutableArray
, который будет содержать объект класса для каждого класса, который мы swizzled.
- (void)tearDown
{
for (id mockToDisable in _mocksToDisable)
{
[mockToDisable disableMock];
}
}
Мы делаем это в tearDown
, чтобы убедиться, что мы unswizzled нашего класса после того, как тест запуска - не сделать это прямо в тесте, потому что, если есть исключение не все ваш тестовый код может получить казнен но tearDown всегда будет.
Могут быть другие макетные технологии, которые делают это более простым, хотя я нашел, что это не так плохо, так как вы пишете его один раз и можете использовать его много раз.