Не может заглушить метод класса с 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);
}

Глядя на ваш пример проекта:

  1. Вы не должны компилировать DetailViewController.m в вашей тестовой цели.

  2. Вы не должны иметь никаких ссылок на OCMock в вашей основной цели.

Я удалил все ссылки на OCMock из обоих проектов, затем просто включил OCMock из исходного кода, и тест прошел нормально. Я думаю, что вы, вероятно, просто имеете некоторые экологические конфликты, которые вызывают вашу проблему.

Хотя ответ Carl Lindberg является правильным, я подумал, что суммирую то, что мы обсуждали в комментариях к его ответу здесь:

  • Проблема была просто в том, что я использовал устаревшую версию OCMock. Причина, по которой я туда попал, была из-за того, что инструкции на странице ocmock просто попросили меня взять пример iOS со своей учетной записи github и скопировать библиотеку OCMock (они даже получили указание использовать ту же структуру каталогов). Оказывается, библиотеке в их примере уже более 2 лет!!,

  • Чтобы исправить это, просто запустите сценарий build.rb в оболочке следующим образом: ruby build.rb, Это даст вам актуальный libOCMock.a библиотека, которую вы можете просто подключить обратно к вашему проекту, и вуаля! все сделано!

Просто используйте

id detailViewMock = [OCMockObject niceMockForClass:[DetailViewController class]];
Другие вопросы по тегам