我在使用Xcode 5的测试时也遇到了不少麻烦。它仍然存在一些奇怪的错误行为,但我已经找到了你的特定的XCTAssertEqual不起作用的明确原因。
如果我们看一下测试代码,我们可以看到它实际上执行以下操作(直接从XCTestsAssertionsImpl.h中获取 - 可以在那里更轻松地查看):
#define _XCTPrimitiveAssertEqual(a1, a2, format...) \
({ \
@try { \
__typeof__(a1) a1value = (a1); \
__typeof__(a2) a2value = (a2); \
NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
float aNaN = NAN; \
NSValue *aNaNencoded = [NSValue value:&aNaN withObjCType:@encode(__typeof__(aNaN))]; \
if ([a1encoded isEqualToValue:aNaNencoded] || [a2encoded isEqualToValue:aNaNencoded] || ![a1encoded isEqualToValue:a2encoded]) { \
_XCTRegisterFailure(_XCTFailureDescription(_XCTAssertion_Equal, 0, @#a1, @#a2, _XCTDescriptionForValue(a1encoded), _XCTDescriptionForValue(a2encoded)),format); \
} \
} \
@catch (id exception) { \
_XCTRegisterFailure(_XCTFailureDescription(_XCTAssertion_Equal, 1, @#a1, @#a2, [exception reason]),format); \
}\
})
问题在于:
测试实际上是将值编码为NSValue
,然后进行比较。你可能会问:“好吧,但这有什么问题?”我之前也没有想到,直到我自己制作了一个测试案例。问题在于,NSValue的-isEqualToValue
方法必须同时比较NSValue的编码类型和实际值。只有两者都相等时该方法才会返回YES
。
在你的情况下,arr.count
是一个NSUInteger
,它是unsigned int
的一个typedef。编译时常量3
在运行时可能退化为signed int
。因此,当将它们放入NSValue
对象中时,它们的编码类型不相等,因此根据-[NSValue isEqualToValue]
,这两个值不能相等。
你可以通过一个自定义示例来证明这一点。下面的代码显式地执行了与XCTAssertEqual
完全相同的操作:
unsigned int a1 = 3;
signed int a2 = 3;
__typeof__(a1) a1value = (a1);
__typeof__(a2) a2value = (a2);
NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))];
NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))];
if (![a1encoded isEqualToValue:a2encoded]) {
NSLog(@"3 != 3 :(");
}
每次日志中都会出现"3 != 3 :("
,这其实是预期的行为。 NSValue
在进行比较时会检查其类型编码。不幸的是,在测试两个(“相等”的)整数时,这不是我们所期望的。
顺便说一下,XCTAssertTrue
的逻辑更加直接,通常表现符合预期(同样,请参阅实际源代码以了解它如何确定断言是否失败)。