web-dev-qa-db-fra.com

Erreur XCTAssertEqual: ("3") n'est pas égal à ("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.");

Qu'est-ce que je ne comprends pas sur XCTAssertEqual? Pourquoi la dernière assertion échoue-t-elle?

37
Sergey

J'ai également eu pas mal de problèmes avec les tests de Xcode 5. Cela semble encore assez bogué avec un comportement étrange - mais j'ai trouvé la raison définitive pour laquelle votre XCTAssertEqual ne fonctionne pas.

Si nous jetons un coup d'œil au code de test, nous voyons qu'il fait en fait ce qui suit (tiré directement de XCTestsAssertionsImpl.h - il peut être plus facile de le voir):

#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); \
    }\
})

Voici le problème:

Ce que le test est en train de faire, c'est de coder les valeurs dans un NSValue, puis de les comparer. "D'accord," dites-vous, "mais quel est le problème avec ça?" Je ne pensais pas qu'il y en avait un non plus jusqu'à ce que j'en fasse mon propre cas de test. Le problème est que -isEqualToValue De NSValue doit également comparer le type d'encodage de NSValue ainsi que sa valeur réelle. Les deux doivent être égaux pour que la méthode renvoie YES.

Dans votre cas, arr.count Est un NSUInteger qui est un typedef de unsigned int. La constante de compilation 3 Dégénère vraisemblablement en signed int Au moment de l'exécution. Ainsi, lorsque les deux sont placés dans un objet NSValue, leurs types de codage ne sont pas égaux et donc les deux NE PEUVENT PAS être égaux selon -[NSValue isEqualToValue].

Vous pouvez le prouver avec un exemple personnalisé. Le code suivant fait explicitement exactement ce que fait 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 :(" Apparaîtra à chaque fois dans le journal.

Je m'empresse d'ajouter ici qu'il s'agit en fait d'un comportement attendu. NSValue est supposé pour vérifier son encodage de type lors des comparaisons. Malheureusement, ce n'est tout simplement pas ce à quoi nous nous attendions lors du test de deux entiers ("égaux").

XCTAssertTrue, d'ailleurs, a une logique beaucoup plus simple et se comporte généralement comme prévu (voir à nouveau la source réelle pour savoir comment elle détermine si l'assertion échoue).

47
Ephemera

J'ai aussi eu ce problème. Comme @ephemera et @napier l'ont indiqué, il s'agit d'un problème de type .

Il peut être résolu en fournissant une valeur du type correct, en utilisant les modificateurs c-literal.

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

Vous pouvez trouver le type correct en recherchant le type de retour de la fonction utilisée sur le côté gauche - ALT-click en arr .count:

- (NSUInteger)count;

Maintenant, ALT-cliquez sur NSUInteger pour trouver son type:

typedef unsigned long NSUInteger;

Maintenant, trouvez le format numérique littéral c pour les longs non signés - Google est un bon ami mais cette page fonctionne:

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

Comme conseil rapide ici, vous devrez peut-être utiliser U (non signé) L (long) ou F (float), et assurez-vous d'écrire 1.0 au lieu de 1 pour obtenir un double. Les minuscules fonctionnent également, comme dans mon exemple ci-dessus.

5
Alex Brown

Dans le cas où quelqu'un d'autre recherche le problème potentiel en comparant deux fois comme moi (la solution ci-dessus ne fonctionnera pas pour float & double), essayez:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);

Génère un échec lorsque (différence entre (\ une expression1) et (\ une expression2) est> (\ une précision))).

3
Kjuly

J'ai également été accroché par ce problème, très reconnaissant pour les solutions de contournement fournies ici. Pour info rapide, il semble que cela ait été corrigé dans la version Xcode 5.1.

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

La macro XCTAssertEqual (anciennement STAssertEquals utilisant OCUnit) compare correctement les valeurs scalaires de différents types sans transtypage, par exemple, int et NSInteger. Il ne peut plus accepter de types non scalaires, tels que des structures, pour comparaison. (14435933)

Je n'ai pas encore mis à niveau à partir de Xcode 5.0.2 mais mon collègue l'a fait, et les mêmes tests XC qui échouaient auparavant en raison de ce problème passent maintenant sans la solution de contournement de casting.

0
Todd Patterson

Une alternative consiste à simplement utiliser le casting:

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

Cela pourrait être la meilleure solution avec l'état actuel des outils, surtout si vous avez du code où vous utilisez beaucoup XCTAssertEqual et que vous ne voulez pas passer à XCTAssertTrue.

(J'ai remarqué que @RobNapier a fait cette suggestion dans un commentaire.)

0
ThomasW