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.
Ответы
Ответ 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 из цели теста, и все пройдет!