41
NSMutableArray *arr = [NSMutableArray array];
[arr addObject:@"1"];
[arr addObject:@"2"];
[arr addObject:@"3"];

// This statement is fine.
XCTAssertTrue(arr.count == 3, @"Wrong array size.");

// This assertion fails with an error: ((arr.count) equal to (3)) failed: ("3") is not equal to ("3")
XCTAssertEqual(arr.count, 3, @"Wrong array size.");

我对 XCTAssertEqual 有什么不了解的地方?为什么最后一个断言失败?

4

5 回答 5

50

我在 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.countis an NSUIntegerwhich 是unsigned int. 编译时常量3可能会在运行时退化为 a signed int。因此,当将两者放入一个NSValue对象时,它们的编码类型不相等,因此根据 ,两者不能相等-[NSValue isEqualToValue]

您可以通过自定义示例来证明这一点。下面的代码明确地做了什么XCTAssertEqual

// Note explicit types
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顺便说一句,它具有更直接的逻辑,并且通常按预期运行(再次,请参阅实际来源以了解它如何确定断言是否失败)。

于 2013-10-04T11:53:35.280 回答
5

我也遇到过这个问题。正如@ephemera 和@napier 所指出的,这是一个类型问题。

它可以通过使用 c-literal 修饰符提供正确类型的值来解决。

XCTAssertEqual(arr.count, 3ul, @"Wrong array size.");

您可以通过查找左侧使用的函数的返回类型来找到正确的类型 -ALT-click在 arr 上。count

- (NSUInteger)count;

现在 ALT-clickNSUInteger找到它的类型:

typedef unsigned long NSUInteger;

现在找到 unsigned long 的 c 文字数字格式 - 谷歌是一个好朋友,但这个页面有效:

http://www.tutorialspoint.com/cprogramming/c_constants.htm

作为此处的快速提示,您可能需要使用 U(无符号)L(长整数)或 F(浮点数),并确保写入 1.0 而不是 1 以获得双精度。小写也可以,就像我上面的例子一样。

于 2013-10-16T22:47:40.733 回答
3

如果其他人正在寻找像我这样的双重比较导致的问题(上面的解决方案不适用于浮动和双重),请尝试:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);

当 ((\a expression1) 和 (\a expression2) 之间的差 > (\a accuracy))) 时生成失败。

于 2016-02-05T03:33:11.217 回答
0

我也被这个问题所困扰,非常感谢这里提供的解决方法。快速仅供参考,这似乎已在 Xcode 5.1 版本中修复。

https://developer.apple.com/library/mac/releasenotes/DeveloperTools/RN-Xcode/xc5_release_notes/xc5_release_notes.html

XCTAssertEqual 宏(以前使用 OCUnit 的 STAssertEquals)可以正确比较不同类型的标量值而无需强制转换,例如 int 和 NSInteger。它不再接受非标量类型(例如结构)进行比较。(14435933)

我还没有从 Xcode 5.0.2 升级,但我的同事已经升级了,并且之前由于这个问题而失败的相同 XC 测试现在在没有强制转换解决方法的情况下通过。

于 2014-04-27T21:18:12.800 回答
0

一种替代方法是仅使用强制转换:

XCTAssertEqual(arr.count, (NSUInteger)3, @"Wrong array size.");

这可能是工具当前状态的最佳解决方案,特别是如果您有XCTAssertEqual大量使用的代码并且不想切换到XCTAssertTrue.

(我注意到@RobNapier 在评论中提出了这个建议。)

于 2014-02-17T01:27:46.337 回答