web-dev-qa-db-ja.com

XCTAssertEqualエラー:( "3")は等しくない( "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.");

XCTAssertEqualについて理解できないことがありますか?最後のアサーションが失敗するのはなぜですか?

37
Sergey

また、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.countunsigned 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 :("は毎回ログに表示されます。

私はここで、これが実際に予想される動作であることを急いで付け加えます。 NSValuesupposedで、比較を行うときにそのタイプエンコーディングをチェックします。残念ながら、2つの(「等しい」)整数をテストするときに期待していたものではありません。

ちなみに、XCTAssertTrueははるかに単純なロジックを備えており、通常どおりに動作します(アサーションが失敗したかどうかを判断する方法については、実際のソースを参照してください)。

47
Ephemera

私もこの問題を抱えています。 @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を記述する必要があります。上記の私の例のように、小文字も機能します。

5
Alex Brown

他の誰かが私のようにダブル比較して問題のリードを探している場合(上記の解決策はフロートとダブルでは機能しません)、試してください:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);

((\ a expression1)と(\ a expression2)の差が>(\ a precision)))の場合にエラーを生成します。

3
Kjuly

私もこの問題に悩まされました。ここで提供されている回避策にとても感謝しています。クイック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テストは、キャスティングの回避策なしで合格しています。

0
Todd Patterson

1つの選択肢は、キャストを使用することです。

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

特にXCTAssertEqualを頻繁に使用しているコードがあり、XCTAssertTrueに切り替えたくない場合は、ツールの現在の状態で最適なソリューションになる可能性があります。

(@RobNapierがコメントでこの提案をしていることに気付きました。)

0
ThomasW