Подтвердить что ты не робот

Ошибка 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? Почему последнее утверждение терпит неудачу?

4b9b3361

Ответ 1

У меня также были проблемы с 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.count является NSUInteger, который является typedef из unsigned int. Постоянная времени компиляции 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 должен проверять кодировку своего типа при выполнении сравнений. К сожалению, это просто не то, что мы ожидали при тестировании двух ( "равных" ) целых чисел.

XCTAssertTrue, кстати, имеет гораздо более простую логику и ведет себя в целом как и ожидалось (опять же, посмотрите на фактический источник того, как он определяет, не выполняется ли утверждение).

Ответ 2

У меня тоже была эта проблема. Как указывали @ephemera и @napier, это проблема типа.

Его можно решить, поставив значение правильного типа, используя модификаторы c-literal.

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

Вы можете найти правильный тип, просмотрев возвращаемый тип функции, используемой в левой части - ALT-click на обр. count:

- (NSUInteger)count;

Теперь ALT-клик по NSUInteger, чтобы найти его тип:

typedef unsigned long NSUInteger;

Теперь найдите буквенный числовой формат c для unsigned long - google - хороший друг, но эта страница работает:

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

В качестве быстрого подсказки здесь вам может понадобиться использовать U (без знака) L (длинный) или F (float), и убедитесь, что вы пишете 1.0 вместо 1, чтобы получить двойной. Строчный регистр также работает, как в моем примере выше.

Ответ 3

В случае, если кто-то ищет проблему с двойным сравнением, как я (решение выше не будет работать для float и double), попробуйте:

XCTAssertEqualWithAccuracy(number.doubleValue, 12.34, 0.01);

Создает сбой, когда (разница между (\ a expression1) и (\ a expression2) равнa > (\ a точность))).

Ответ 4

Один из вариантов - просто использовать кастинг:

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

Это может быть лучшее решение с текущим состоянием инструментов, особенно если у вас есть код, в котором вы часто используете XCTAssertEqual и не хотите переключаться на XCTAssertTrue.

(Я заметил, что @RobNapier сделал это предложение в комментарии.)

Ответ 5

Я тоже зацепил этот вопрос, очень благодарен за обходные пути, предоставленные здесь. Quick FYI, похоже, это было исправлено в выпуске Xcode 5.1.

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

Макрос XCTAssertEqual (ранее STAssertEquals с использованием OCUnit) корректно сравнивает скалярные значения разных типов без каста, например int и NSInteger. Он больше не может принимать нескалярные типы, такие как структуры, для сравнения. (14435933)

Я еще не обновился с Xcode 5.0.2, но у моего коллеги есть, и те же тесты XC, которые ранее были неудачными из-за этой проблемы, теперь передаются без обходного пути.