Не может заглушить метод класса с OCMock 2.1+ в Xcode 5.0
Я знаю, что OCMock версии 2.1+ поддерживает готовые методы класса-заглушки. Но по какой-то причине это не работает со мной. Чтобы убедиться, что я изолировал проблему, я просто клонировал пример проекта OCMock (который явно помечен как версия 2.2.1) и просто добавил его в testMasterViewControllerDeletesItemsFromTableView:
id detailViewMock = [OCMockObject mockForClass:[DetailViewController class]];
[[[detailViewMock stub] andReturn:@"hello"] helloWorld];
в DetailViewController.h
Я добавил:
+ (NSString *)helloWorld;
а также DetailViewController.m
:
+ (NSString *)helloWorld {
return @"hello world";
}
Но я продолжаю получать ошибку:
*** -[NSProxy doesNotRecognize Selector:helloWorld] called!
чтобы увидеть демонстрацию проблемы, пожалуйста, клонируйте этот репозиторий, чтобы увидеть, что происходит.
5 ответов
Это должно работать просто отлично. Я только что попробовал в моем проекте, который использует XCTest на XCode5, и этот код прошел.
Я хотел бы 1) убедиться, что вы используете последнюю версию OCMock (сейчас это 2.2.1; я думаю, что есть некоторые исправления как для методов класса, так и для Xcode5 в более новых версиях), и 2) убедиться, что ваш класс DetailViewController связаны во время выполнения (т. е. часть правильной цели) правильно.
Рассматривая ваш проект, ваш класс DetailViewController является частью как основного приложения, так и цели тестирования. Похоже, что в Xcode5 это означает, что две копии класса скомпилированы и присутствуют во время выполнения, при этом код в приложении вызывает одну копию, а код в тестовом примере вызывает другую. Раньше это была ошибка компоновщика (повторяющиеся символы), но, к лучшему или худшему, теперь компоновщик, по-видимому, позволяет молча допустить существование двух копий одного класса (с одинаковым именем) во время выполнения ObjC. OCMock, используя динамический поиск, находит первый (тот, который скомпилирован в приложение), но тестовый пример напрямую связан со второй копией (тот, который скомпилирован в комплект тестов). Итак... OCMock на самом деле не издевается над классом, о котором вы думаете.
Вы можете убедиться в этом, просто для удобства, проверив в рамках теста, что класс [DetailViewController] не будет равен NSClassFromString(@"DetailViewController") (первый напрямую связан, второй - динамический).
Чтобы исправить это должным образом, в разделе "Целевое членство" для DetailViewController.m просто снимите флажок с контрольной цели. Таким образом, во время выполнения остается только одна копия класса, и все работает так, как вы ожидаете. Тестовый пакет загружается в основное приложение, поэтому все классы основного приложения должны быть доступны для пакета без необходимости их прямой компиляции в пакет. Классы должны быть только частью одной из двух целей, а не обеих (это всегда было так).
Не могли бы вы показать код, который вы тестируете? Это работает:
@interface DetailViewController : UIViewController
+ (NSString *) helloWorld;
@end
@implementation DetailViewController
+ (NSString *)helloWorld
{
return @"hello world";
}
@end
Тест:
- (void) test__stubbing_a_class_method
{
id mockDetailViewController = [OCMockObject mockForClass:[DetailViewController class]];
[[[mockDetailViewController stub] andReturn:@"hello"] helloWorld];
STAssertEqualObjects([DetailViewController helloWorld], @"hello", nil);
}
Глядя на ваш пример проекта:
Вы не должны компилировать DetailViewController.m в вашей тестовой цели.
Вы не должны иметь никаких ссылок на OCMock в вашей основной цели.
Я удалил все ссылки на OCMock из обоих проектов, затем просто включил OCMock из исходного кода, и тест прошел нормально. Я думаю, что вы, вероятно, просто имеете некоторые экологические конфликты, которые вызывают вашу проблему.
Хотя ответ Carl Lindberg является правильным, я подумал, что суммирую то, что мы обсуждали в комментариях к его ответу здесь:
Проблема была просто в том, что я использовал устаревшую версию OCMock. Причина, по которой я туда попал, была из-за того, что инструкции на странице ocmock просто попросили меня взять пример iOS со своей учетной записи github и скопировать библиотеку OCMock (они даже получили указание использовать ту же структуру каталогов). Оказывается, библиотеке в их примере уже более 2 лет!!,
Чтобы исправить это, просто запустите сценарий build.rb в оболочке следующим образом:
ruby build.rb
, Это даст вам актуальныйlibOCMock.a
библиотека, которую вы можете просто подключить обратно к вашему проекту, и вуаля! все сделано!
Просто используйте
id detailViewMock = [OCMockObject niceMockForClass:[DetailViewController class]];