18

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

代码是:

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

我逐步浏览了代码并做了:

po self.detailItem

显示:

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

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

编辑:

这是 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];
    } 
}

它是来自 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.
4

10 回答 10

16

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

于 2012-08-28T14:13:43.680 回答
2

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

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

于 2012-08-28T13:13:22.687 回答
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])在调试器中运行时将返回您期望的值。

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

于 2014-08-06T16:57:39.943 回答
1

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

但是,如果您将应用程序作为测试目标的目标依赖项(如果您的测试目标中只有测试源文件,而不是项目源文件),那么您遇到的问题与我相同:您的一个类应该在 App 目标中,而不是在测试目标中(在我的情况下,它是一个测试助手类!)

于 2014-09-04T13:04:43.970 回答
1

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

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

正如这里提到的,您不应该在两个目标中都包含被测对象。仅将对象放在一个目标中将阻止意外行为isKindOfClass:

此外,我的一位同事发现您需要确保为“运行”构建关闭单元测试。在应用程序的方案下,选择“构建”,然后查看单元测试目标,应该取消选择“运行”。

如果您使用 Swift 进行开发,那么您@testable import MyModule的单元测试文件的顶部应该有一个。

于 2015-11-16T05:05:44.507 回答
0

可以self.detailItemnil?的结果-isKindOfClass:将是NO在那种情况下。

于 2012-08-28T13:24:44.020 回答
0

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

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

自从

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

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

于 2014-01-14T23:13:04.717 回答
0

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

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

于 2015-10-04T11:21:06.983 回答
0

SomeClass使用 CocoaPods 1.0(在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

于 2017-02-15T07:34:23.707 回答
0

在此链接中找到了解决方案:

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

从测试目标中删除 SomeEntity ,一切都通过了!

于 2019-07-25T20:14:26.763 回答