XCTAssertEqual错误:(“3”)不等于(“3”)

时间:2013-10-04 09:42:51

标签: objective-c ocunit xctest

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不了解什么?为什么最后一个断言失败了?

5 个答案:

答案 0 :(得分:47)

我对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.countNSUInteger,其类型定义为unsigned int。编译时常量3可能在运行时退化为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具有更直接的逻辑,并且通常表现得如预期的那样(再次,请参阅确定断言是否失败的实际来源)。

答案 1 :(得分:5)

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

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

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

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

- (NSUInteger)count;

现在ALT点击NSUInteger以查找其类型:

typedef unsigned long NSUInteger;

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

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

作为这里的快速提示,您可能需要使用U(无符号)L(长整数)或F(浮点数),并确保编写1.0而不是1来获得双精度。小写也可以,如上例所示。

答案 2 :(得分:3)

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

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);
  

((一个表达式1)和(\一个表达式2)之间的差异是>(\一个精度)))时产生失败。

答案 3 :(得分:0)

另一种方法是使用铸造:

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

对于当前工具状态,它可能是最佳解决方案,尤其是如果您有大量使用XCTAssertEqual并且不想切换到XCTAssertTrue的代码。

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

答案 4 :(得分:0)

我也被这个问题所困扰,非常感谢这里提供的解决方法。 快速FYI,似乎已在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测试现在没有使用强制转换方法。