2013-10-04 6 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 error: ("3") не равно ("3")

+2

Некоторые другие большие библиотеки Сличитель являются: OCHamcrest и Expecta. , есть также Kiwi и Cedar - полноценные тестовые рамки с красивыми встроенными библиотеками-соломерами. , (На всякий случай вы еще не пробовали их). –

ответ

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's -isEqualToValue также должен сравнить тип кодировки NSValue типа, а также его фактическое значение. Оба метода должны быть равны для возвращаемого метода YES.

В вашем случае arr.count является NSUInteger который является typedef unsigned int. Постоянная времени компиляции 3 предположительно вырождается в signed int во время выполнения. Таким образом, когда эти два объекта помещаются в объект NSValue, их типы кодирования не равны и, следовательно, два CAN NOT не равны в соответствии с -[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, между прочим, имеет гораздо более простую логику и ведет себя в целом как и ожидалось (опять же, посмотрите на фактический источник того, как он определяет, не выполняется ли утверждение).

+27

Стоит отметить, что правильным решением для этого является простое включение информации о типе. 'XCTAssertEqual (arr.count, (NSUInteger) 3, @" Неверный размер массива. ");' –

+5

Спасибо, еще проще: 'XCTAssertEqual (arr.count, 3U, @" Неверный размер массива. ");' – owenfi

+3

Лучше использовать '(NSUInteger) 3', а не' 3U', потому что NSUInteger typedef'ed по-разному для 64-битной и 32-битной компиляции. Для 64-битного NSUInteger - это 'unsigned long' или' unsigned int' для 32 бит. – zim

5

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

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

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

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

- (NSUInteger)count; 

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

typedef unsigned long NSUInteger; 

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

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

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

+0

Я не думаю, что это работает, если вы хотите, чтобы ваши тесты работали как на 32, так и на 64 бит. Использование '3ul' приведет к сбою с 32 бит. – ThomasW

0

Одним из вариантов являются просто использовать Активирование:

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

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

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

0

Я получил зацепил по этому вопросу, а также, очень благодарен за обходные предоставленные здесь. Quick FYI, похоже, это было исправлено в выпуске 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))).