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について理解できないことがありますか?最後のアサーションが失敗するのはなぜですか?
また、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
にエンコードしてから比較することです。 「わかりました」とあなたは言います、「しかしそれは何の問題ですか?」独自のテストケースを作成するまで、1つもあるとは思いませんでした。問題は、NSValueの-isEqualToValue
は、NSValueのエンコーディングタイプと実際の値も比較する必要があることです。 Bothは、メソッドがYES
を返すために等しくなければなりません。
あなたの場合、arr.count
はunsigned int
のtypedefであるNSUInteger
です。コンパイル時定数3
は、おそらく実行時にsigned int
に縮退します。したがって、2つがNSValue
オブジェクトに入れられると、それらのエンコードタイプは等しくないため、2つは-[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
はsupposedで、比較を行うときにそのタイプエンコーディングをチェックします。残念ながら、2つの(「等しい」)整数をテストするときに期待していたものではありません。
ちなみに、XCTAssertTrue
ははるかに単純なロジックを備えており、通常どおりに動作します(アサーションが失敗したかどうかを判断する方法については、実際のソースを参照してください)。
私もこの問題を抱えています。 @ephemeraと@napierが示したように、これはtypeの問題です。
C-literal修飾子を使用して、正しい型の値を指定することで解決できます。
XCTAssertEqual(arr.count, 3ul, @"Wrong array size.");
左側で使用されている関数の戻り値の型を調べることにより、正しい型を見つけることができます-ALT-click
arr .count
で:
- (NSUInteger)count;
Altキーを押しながらNSUInteger
をクリックして、そのタイプを見つけます。
typedef unsigned long NSUInteger;
次に、unsigned longのcリテラル数値形式を見つけます-Googleは良い友達ですが、このページは機能します:
http://www.tutorialspoint.com/cprogramming/c_constants.htm
ここでの簡単なヒントとして、U(符号なし)L(長い)またはF(浮動)を使用する必要があります。また、倍精度を得るには、1ではなく1.0を記述する必要があります。上記の私の例のように、小文字も機能します。
他の誰かが私のようにダブル比較して問題のリードを探している場合(上記の解決策はフロートとダブルでは機能しません)、試してください:
XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);
((\ a expression1)と(\ a expression2)の差が>(\ a precision)))の場合にエラーを生成します。
私もこの問題に悩まされました。ここで提供されている回避策にとても感謝しています。クイックFYI、これはXcode 5.1リリースで修正されたようです。
XCTAssertEqualマクロ(以前はOCUnitを使用したSTAssertEquals)は、intやNSIntegerなど、キャストせずに異なるタイプのスカラー値を正しく比較します。比較のために、構造体などの非スカラー型を受け入れることはできなくなりました。 (14435933)
私はまだXcode 5.0.2からアップグレードしていませんが、私の同僚は持っています。この問題が原因で以前失敗していたXCテストは、キャスティングの回避策なしで合格しています。
1つの選択肢は、キャストを使用することです。
XCTAssertEqual(arr.count, (NSUInteger)3, @"Wrong array size.");
特にXCTAssertEqual
を頻繁に使用しているコードがあり、XCTAssertTrue
に切り替えたくない場合は、ツールの現在の状態で最適なソリューションになる可能性があります。
(@RobNapierがコメントでこの提案をしていることに気付きました。)