IsKindOfClass возвращает NO неожиданно - программирование

IsKindOfClass возвращает NO неожиданно

Один из моих модульных тестов терпит неудачу и по какой-то причине я не ожидал. Кажется, что вызов isKindOfClass возвращает NO, но когда я отлаживаю и перешагиваю, кажется, нет причин, по которым это было бы.

Код:

if ([self.detailItem isKindOfClass:[MovieInfo class]]) {
    [self configureViewForMovie];
}

Я прошел через код и сделал:

po self.detailItem

который отображает:

(id) $1 = 0x0ea8f390 <MovieInfo: 0xea8f390>

Итак, что мне не хватает, почему оператор if возвращает false в этом случае?

EDIT:

Вот настройка для элемента DetailItem:

- (void)setDetailItem:(id)newDetailItem
{
    if (_detailItem != newDetailItem) {
        NSLog(@"%@", [newDetailItem class]);
        _detailItem = newDetailItem;

        // Update the view.
        [self configureView];
    }

    if (self.masterPopoverController != nil) {
        [self.masterPopoverController dismissPopoverAnimated:YES];
    } 
}

Код шаблона из шаблона мастера.

unit test создает MovieInfo в setUp:

movie = [[MovieInfo alloc] initWithMovieName:@"Movie" movieID:1];

и устанавливает его в тесте

controller.detailItem = movie;

Кроме того, я добавил утверждение параметра в setDetailItem:

NSParameterAssert([newDetailItem isKindOfClass:[MovieInfo class]] || [newDetailItem isKindOfClass:[PersonInfo class]] || newDetailItem == nil);

Это утверждение также не работает.

Я поставил два оператора журнала над вызовом подтверждения:

NSLog(@"%@", [newDetailItem class]);
NSLog(@"%@", newDetailItem);

которые отображают:

2012-08-28 08:31:37.574 Popcorn[8006:c07] MovieInfo
2012-08-28 08:31:38.253 Popcorn[8006:c07] <MovieInfo: 0x6daac50>

ОДИН БОЛЬШЕ РЕДАКТИРОВАНИЯ:

Я добавил проверку isKindOfClass перед тем, как установить ее в unit test, которая проходит.

if ([movie isKindOfClass:[MovieInfo class]]) {
    NSLog(@"Yep"); //This passes and prints out
}
controller.detailItem = movie; //calls into the setter and fails.
4b9b3361

Ответ 1

Это было связано с тем, что класс под тестом "DetailViewController" не находился в тестовой цели. Я бы ожидал, что это проявится по-другому (ошибка компоновщика или что-то еще), но, по-видимому, это просто вызывает странное поведение. Добавление элемента DetailViewController в тестовую цель устраняет проблемы.

Ответ 2

Я бы заподозрил состояние гонки или изменил настройки Debug и Release. Это приведет к различиям между отладчиком и регулярным временем выполнения.

Сначала убедитесь, что self.detailItem не nil. Это самая распространенная причина такого рода проблем. Затем попробуйте отладить это с помощью журнала, а не отладчика. Если у вас действительно есть условия гонки, вы можете также рассмотреть возможность регистрации с printf(), а не NSLog(). printf() - это гораздо менее эффективный способ ведения многопоточных протоколирования.

Ответ 3

У меня была такая же проблема в библиотеке, которую я пишу. Как ни странно, он отлично работал на моей целевой тестовой программе iOS, но не смог выполнить тестовый тест Mac (интересно!).

Я считаю, что проблема связана с несоответствием в объявлениях классов. Библиотека использует декларацию .h, но модульные тесты рассматривали внутренние декларации.

Это проще объяснить с помощью примера:

/*
 * ClassA.h
 */
@interface ClassA () : NSObject

@property (nonatomic, strong, readonly) NSString *someValue1;
@property (nonatomic, strong, readonly) NSString *someValue2;
@property (nonatomic, strong, readonly) NSString *someValue3;

@end

Добавление в список свойств в .m

/*
 * ClassA.m
 */
@interface ClassA () : NSObject

@property (nonatomic, strong, readwrite) NSString *someValue2;
@property (nonatomic, strong, readwrite) NSDate *date;
@property (nonatomic, strong, readwrite) NSDateFormatter *dateFormatter;

@end

@implementation 
// implementation
@end

Теперь, поскольку в объектах unit test были установлены источники компиляции для включения ClassA.m, isKindOfClass: возвращал no, но команда po и NSStringFromClass([ClassA class]) при запуске в отладчике вернули ожидаемые значения.

Я знаю, что это старый пост, но я надеюсь, что это полезно и сэкономит кому-то много времени. Принял меня почти час, пытаясь понять эту проблему!

Ответ 4

Ответ MarkPowell помогает абсолютно, если вы хотите всегда добавлять все исходные файлы во все ваши тестовые цели.

Однако, если у вас есть приложение как целевая зависимость вашей тестовой цели (если у вас есть только исходные исходные файлы теста, а не исходные файлы проекта), то у вас есть те же проблемы, что и у меня: один из ваши классы должны находиться в целевом приложении, а не в тестовой цели (в моем случае это был класс тестового помощника!)

Ответ 5

Как уже упоминалось @stefreak, в стандартной конфигурации модульного тестирования тестируемый объект не должен включаться в цель модульного тестирования. В этой ситуации, если вы включаете тестируемый объект в цель теста, при сборке в журнал записывается следующий тип сообщения:

Class foo is implemented in both <.app path> and <.xctest path>. One of the two will be used. Which one is undefined.

Как уже упоминалось здесь, вы не должны включать тестируемый объект в обе цели. Наличие объекта только в одной цели остановит неожиданное поведение с isKindOfClass:.

Также мой коллега обнаружил, что вам нужно убедиться, что юнит-тесты отключены для сборок "Выполнить". Под схемой для приложения выберите "Build", затем посмотрите на цель модульного теста, "Run" должен быть отменен.

Если вы разрабатываете в Swift, то у вас должен быть @testable import MyModule вверху файла модульного теста.

Ответ 6

Может ли self.detailItem быть nil? Результатом -isKindOfClass: будет NO в этом случае.

Ответ 7

То же самое происходит со мной. Я закончил сравнение следующим образом:

[self.detailItem class]  == [ETTWallpaper class]

Так как

[[self.detailItem class] isKindOfClass:[ETTWallpaper class]]

Не работал и всегда возвращал NO, несмотря на то, что он должен был вернуть YES

Ответ 8

Я думаю, что в настоящее время есть ошибка в Xcode 7.0. У меня такая же проблема, и проверены все CUT включены в тестовую цель, но у меня есть код, который в коде возвращает NO to -[isKindOfClass:], но в отладчике возвращается YES. И это происходит в симуляторах и физических устройствах.

Я закончил проверку -[respondsToSelector:], чтобы проверить подпись моего класса. Не идеально, но позволяет мне пахать.

Ответ 9

У меня такая же проблема при запуске unit test с CocoaPods 1.0 (где SomeClass находится внутри библиотеки Pod)

Появилось следующее предупреждающее сообщение:

Class SomeClass is implemented in both </path/to/myapp> and </path/to/myapptest>. One of the two will be used. Which one is undefined.

Мое решение состоит в том, чтобы обновить подфайл до:

target "MyApp" do
  pod 'xxx'
  pod 'yyy'

  target "MyApp-Tests" do
      inherit! :search_paths
  end
end

Литература: https://github.com/CocoaPods/CocoaPods/issues/4626

Ответ 10

Нашел решение по этой ссылке :

Мой класс SomeEntity был включен в цель теста. Создание цели теста также включает в себя основное приложение в качестве зависимости, которая также включает SomeEntity. Это, очевидно, заставляет XCode полагать, что есть 2 различных типа.

Удалите SomeEntity из цели теста, и все пройдет!