简体   繁体   English

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

[英]XCTAssertEqual error: (“3”) is not equal to (“3”)

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.");

What don't I understand about XCTAssertEqual? 我对XCTAssertEqual不了解什么? Why does the last assertion fail? 为什么最后一个断言失败了?

I have also had quite a bit of trouble with Xcode 5's tests. 我对Xcode 5的测试也遇到了很多麻烦。 It still seems quite buggy with some odd behaviour - however I have found the definitive reason your particular XCTAssertEqual isn't working. 它仍然看起来很奇怪有一些奇怪的行为 - 但是我已经找到了你的特定XCTAssertEqual的明确原因。

If we have a look at the test code we see it actually does the following (taken directly from XCTestsAssertionsImpl.h - it may be easier to view there): 如果我们看一下测试代码,我们会看到它实际执行以下操作(直接从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); \
    }\
})

Here's the problem: 这是问题所在:

What the test is actually doing is encoding the values into an NSValue and then comparing them. 测试实际上做的是将值编码到NSValue然后比较它们。 "Okay," you say, "but what's the problem with that?" “好的,”你说,“但那有什么问题?” I didn't think there was one either until I made my own test case for it. 在我为自己制作测试用例之前,我认为没有一个。 The problem is that NSValue's -isEqualToValue must also compare the NSValue's encoding type as well as its actual value. 问题是NSValue的-isEqualToValue还必须比较NSValue的编码类型及其实际值。 Both must be equal for the method to return YES . 两者必须相等才能返回YES

In your case, arr.count is an NSUInteger which is a typedef of unsigned int . 在您的情况下, arr.count是一个NSUInteger ,它是unsigned int的typedef。 The compile-time constant 3 presumably degenerates into a signed int at runtime. 编译时常量3可能在运行时退化为signed int Thus when the two are put into an NSValue object, their encoding types are not equal and thus the two CANNOT be equal according to -[NSValue isEqualToValue] . 因此,当两者放入NSValue对象时,它们的编码类型不相等,因此根据-[NSValue isEqualToValue] ,两者不能相等。

You can prove this with a custom example. 您可以使用自定义示例来证明这一点。 The following code explicitly does exactly what XCTAssertEqual does: 以下代码显式执行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 :(" will appear in the log every time. "3 != 3 :("每次都会出现在日志中。

I hasten to add here that this is, in fact, expected behaviour. 我赶紧在这里补充一点,事实上,这是预期的行为。 NSValue is supposed to check its type encoding when doing comparisons. NSValue 应该在进行比较时检查其类型编码。 Unfortunately it's just not what we were expecting when testing two ('equal') integers. 不幸的是,这不是我们在测试两个('相等')整数时所期望的。

XCTAssertTrue , incidentally, has much more straightforward logic, and behaves generally as expected (again, see the actual source for how it determines whether the assertion fails). XCTAssertTrueXCTAssertTrue具有更直接的逻辑,并且通常表现得如预期的那样(同样,请参见实际来源,了解它如何确定断言是否失败)。

I have had this problem, too. 我也遇到过这个问题。 As @ephemera and @napier indicated, this is a type issue. 正如@ephemera和@napier所说,这是一个类型问题。

It can be solved by supplying a value of the correct type, using the c-literal modifiers. 它可以通过使用c-literal修饰符提供正确类型的值来解决。

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

You can find the correct type by looking up the return type of the function used on the left hand side - ALT-click on arr. 您可以通过查找左侧使用的函数的返回类型找到正确的类型 - ALT-click arr。 count : count

- (NSUInteger)count;

Now ALT-click on NSUInteger to find its type: 现在ALT-单击NSUInteger以查找其类型:

typedef unsigned long NSUInteger;

Now find the c literal numeric format for unsigned long - google is a good friend but this page works: 现在找到unsigned long的c文字数字格式 - 谷歌是一个好朋友,但这个页面有效:

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

As a quick hint here, you may need to use U (unsigned) L (long) or F (float), and make sure you write 1.0 instead of 1 to get a double. 作为这里的快速提示,您可能需要使用U(无符号)L(长整数)或F(浮点数),并确保编写1.0而不是1来获得双精度。 Lowercase also works, as in my example above. 小写也可以,如上例所示。

In case someone else is looking for the issue lead by double comparing like me (solution above won't work for float & double), try: 如果其他人通过像我这样的双重比较寻找问题引导(上面的解决方案不适用于float和double),请尝试:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);

Generates a failure when (difference between (\\a expression1) and (\\a expression2) is > (\\a accuracy))). 当((\\ _表达式1)和(\\ a表达式2)之间的差异>(\\ a精度))时生成失败。

One alternative is to just use casting: 另一种方法是使用铸造:

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

It might be the best solution with the current state of the tools, especially if you have code where you're using XCTAssertEqual a lot and don't want to switch to XCTAssertTrue . 它可能是当前工具状态的最佳解决方案,特别是如果你有很多代码,你很多地使用XCTAssertEqual并且不想切换到XCTAssertTrue

(I noticed @RobNapier made this suggestion in a comment.) (我注意到@RobNapier在评论中提出了这个建议。)

I got snagged by this issue as well, very thankful for the workarounds provided here. 我也被这个问题所困扰,非常感谢这里提供的解决方法。 Quick FYI, it seems this was fixed in Xcode 5.1 release. 快速FYI,似乎这已在Xcode 5.1版本中得到修复。

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

The XCTAssertEqual macro (formerly STAssertEquals using OCUnit) correctly compares scalar values of different types without casting, for example, int and NSInteger. XCTAssertEqual宏(以前使用OCUnit的STAssertEquals)正确地比较不同类型的标量值而不进行强制转换,例如int和NSInteger。 It can no longer accept nonscalar types, such as structs, for comparison. 它不能再接受非标量类型,例如结构,以进行比较。 (14435933) (14435933)

I have not yet upgraded from Xcode 5.0.2 but my colleague has, and the same XC tests that were previously failing due to this problem are now passing without the casting workaround. 我还没有从Xcode 5.0.2升级,但是我的同事已经升级了,之前由于这个问题导致失败的XC测试现在没有使用强制转换方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM