isKindOfClass意外返回NO

时间:2012-08-28 13:08:33

标签: objective-c cocoa

我的一个单元测试失败了,原因是我没有预料到。似乎对isKindOfClass的调用返回NO,但是当我调试并逐步执行时,似乎没有理由这样做。

代码是:

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

我完成了代码并做了:

po self.detailItem

显示:

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

那么,我错过了什么,为什么在这种情况下if语句会返回false?

编辑:

这是DetailItem的setter:

- (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];
    } 
}

它是Master Detail模板中的模板代码。

单元测试在setUp:

中创建一个MovieInfo
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检查之前添加了if ([movie isKindOfClass:[MovieInfo class]]) { NSLog(@"Yep"); //This passes and prints out } controller.detailItem = movie; //calls into the setter and fails. 检查。

{{1}}

10 个答案:

答案 0 :(得分:16)

这是因为被测试的类“DetailViewController”不在测试的目标中。我原以为这会以不同的方式表现出来(链接器错误或其他东西),但显然,它只会导致奇怪的行为。将DetailViewController添加到测试目标可以解决问题。

答案 1 :(得分:2)

我怀疑是竞争条件,还是调试和发布配置之间存在差异。这些将导致调试器和常规运行时之间的差异。

首先,确保self.detailItem不是nil。这是此类问题的最常见原因。然后尝试使用日志而不是调试器来调试它。如果您确实有竞争条件,那么您也可以考虑使用printf()而不是NSLog()进行记录。 printf()是一种影响性能的方法,可用于进行多线程日志记录。

答案 2 :(得分:2)

我在写作的图书馆里遇到了同样的问题。有趣的是,它在我的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

现在,由于单元测试目标的编译源设置为包含ClassA.m,isKindOfClass:将返回no,但po命令和NSStringFromClass([ClassA class])在调试器中运行时返回您期望的值。

我知道这是一个老帖子,但我希望这很有用,可以节省很多时间。我花了差不多一个小时试图解决这个问题!

答案 3 :(得分:1)

如果您希望始终将所有源文件添加到所有测试目标中,MarkPowell的答案绝对有帮助。

但是,如果您将Application作为测试目标的目标依赖项(如果测试目标中只有测试源文件,而不是项目源文件),那么您遇到了同样的问题:你的类应该在App目标中,而不是测试目标(在我的例子中,它是一个测试助手类!)

答案 4 :(得分:1)

如@stefreak所述,在标准单元测试配置中,测试对象不应包含在单元测试目标中。在这种情况下,如果确实在测试目标中包含了测试对象,则在构建时会将以下类型的消息写入日志:

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

正如前面提到的here,你不应该在两个目标中都包含被测对象。仅将对象放在一个目标中将使用isKindOfClass:停止意外行为。

此外,我的一位同事发现你需要确保关闭单元测试&#34;运行&#34;建立。根据应用程序,选择&#34; Build&#34;,然后查看单元测试目标,&#34;运行&#34;应该被取消选择。

如果你在Swift中开发,那么你的单元测试文件顶部应该有一个@testable import MyModule

答案 5 :(得分:0)

self.detailItem可以nil吗?在这种情况下,-isKindOfClass:的结果为NO

答案 6 :(得分:0)

同样的情况发生在我身上。 我最终做了如下比较:

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

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

没有工作,并且总是返回NO,尽管确定它必须返回YES

答案 7 :(得分:0)

我认为目前Xcode 7.0中存在一个错误。我有同样的问题,并检查所有CUT都包含在测试目标中,但我有一个在代码中返回NO-[isKindOfClass:],但在调试器中返回YES。这种情况发生在模拟器和物理设备中。

我最终检查了-[respondsToSelector:]以检查我的班级签名。不完美,但允许我通过。

答案 8 :(得分:0)

使用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.

我的解决方案是将Podfile更新为:

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

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

参考文献: https://github.com/CocoaPods/CocoaPods/issues/4626

答案 9 :(得分:0)

在此link中找到了解决方案:

  

我的SomeEntity类包含在测试目标中。构建测试目标还包括将主应用程序作为依赖项,其中还包括SomeEntity。显然,这使Xcode相信有2种不同的类型。

     

从测试目标中删除SomeEntity,一切顺利!