2013-10-04 32 views
37
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有什麼瞭解?爲什麼最後的斷言失敗?XCTAssertEqual錯誤:(「3」)不等於(「3」)

+2

其他一些偉大的匹配庫是: OCHamcrest和Expecta。 。還有Kiwi和Cedar--完全成熟的測試框架,內置良好的matcher庫。 。 (以防萬一你還沒有嘗試過)。 –

回答

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的typedef。編譯時常量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假設比較時檢查其類型編碼。不幸的是,這並不是我們在測試兩個('相等')整數時所期待的。順便說一句,有更直接的邏輯,並且行爲如預期般一樣(同樣,請參閱實際來源以確定斷言是否失敗)。

+27

值得注意的是,正確的解決方法是簡單地包含類型信息。 'XCTAssertEqual(arr.count,(NSUInteger)3,@「Wrong array size。」);' –

+5

謝謝,更簡單的方法是:'XCTAssertEqual(arr.count,3U,@「數組大小錯誤。 – owenfi

+3

更好地使用'(NSUInteger)3'而不是'3U',因爲對於64bit和32bit編譯,NSUInteger的輸入格式有所不同。對於64位,NSUInteger是32位的「unsigned long」與「unsigned int」。 – zim

5

我也有這個問題。正如@ephemera和@napier所示,這是一個類型問題。

它可以通過使用c-literal修飾符提供正確類型的值來解決。

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

您可以通過查找左手側使用的函數的返回類型的正確類型 - 在編曲ALT-clickcount

- (NSUInteger)count; 

現在在NSUInteger按住Alt鍵找到其類型:

typedef unsigned long NSUInteger; 

現在找到的C字面數字格式無符號長 - 谷歌是一個很好的朋友,但這個頁面的工作原理:

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

作爲這裏的一個快速提示,您可能需要使用U(無符號)L(long)或F(float),並確保您寫入1.0而不是1來獲得雙倍。小寫字母也有效,就像我上面的例子。

+0

如果你想讓你的測試工作在32位和64位,我不認爲這是有效的。使用'3ul'會導致32位故障。 – ThomasW

0

一種替代方法是隻需使用鑄造:

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

這可能與工具的當前狀態最好的解決方案,特別是如果你有,你正在使用XCTAssertEqual大量的代碼和不想切換到XCTAssertTrue

(我注意到@RobNapier在評論提出這個建議。)

0

我被這個問題陷入僵局,以及,在這裏提供的解決方法非常感謝。快速的參考,看來這是在Xcode 5.1版本中修復的。

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. It can no longer accept nonscalar types, such as structs, for comparison. (14435933)

我還沒有在Xcode 5.0.2的升級,但我的同事了,而現在,以前沒有因這一問題同XC測試不通過鑄造的解決方法。

3

萬一別人是尋找雙比較像我這樣的問題,鉛(解決方案上面不會浮法&雙工作),嘗試:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01); 

Generates a failure when (difference between (\a expression1) and (\a expression2) is > (\a accuracy))).