Насмешливый КВО с OCMock - PullRequest
       26

Насмешливый КВО с OCMock

4 голосов
/ 07 марта 2012

Я хотел бы проверить, что Key-Value-Observation правильно работает для моего класса. У него есть одно свойство, которое зависит от другого. Они настроены так:

+ (NSSet *)keyPathsForValuesAffectingSecondProperty {
    return [NSSet setWithObjects:
            @"firstProperty",
            nil];
}

- (NSArray *)secondProperty {
    return [self.firstProperty array];
}

Я хочу запустить модульный тест, чтобы убедиться, что при изменении firstProperty объект, связанный с secondProperty, получает уведомление. Сначала я думал, что смогу использовать +[OCMockObject observerMock], но похоже, что это можно использовать только с NSNotificationCenter. Как лучше всего это проверить?

Ответы [ 3 ]

7 голосов
/ 08 марта 2012

Я работал над этим некоторое время после того, как ответ @ chrispix вдохновил меня работать в другом направлении. Я начал с этого:

id objectToObserve = [[TheClassBeingTested alloc] init];

id secondPropertyObserver = [OCMockObject mockForClass:[NSObject class]];
[[secondPropertyObserver expect] observeValueForKeyPath:@"secondProperty"
                                               ofObject:objectToObserve
                                                 change:OCMOCK_ANY
                                                context:[OCMArg anyPointer]];
[objectToObserve addObserver:secondPropertyObserver
                  forKeyPath:@"secondProperty"
                     options:NSKeyValueObservingOptionNew
                     context:NULL];

// Do something to modify objectToObserve's firstProperty    

[secondPropertyObserver verify];

Когда я запустил этот тестовый код, я получил следующее сообщение:

OCMockObject[NSObject]: unexpected method invoked: isKindOfClass:<??> 
    expected:     observeValueForKeyPath:@"firstProperty" ofObject:

Я провел некоторое исследование и обнаружил, что вызов -isKindOfClass: фиктивного объекта, который не ожидал, был передан объекту класса NSKeyValueObservance.

Я попытался добавить следующий код, чтобы смоделировать ответ, но значения YES и NO оба терпят неудачу с EXC_BAD_ACCESS исключениями с NSKeyValueWillChange в стеке.

BOOL returnVal = NO;
[[[secondPropertyObserver stub] andReturnValue:OCMOCK_VALUE(returnVal)] isKindOfClass:[OCMArg any]];

Я сделал более осторожный шаг и обнаружил, что мой код не вызывал этого исключения - это было во время слива autoreleasepool. Затем меня осенило, что мне нужно убрать наблюдателя. Ниже приведено полное решение, включая удаление наблюдателя.

id objectToObserve = [[TheClassBeingTested alloc] init];

id secondPropertyObserver = [OCMockObject mockForClass:[NSObject class]];

BOOL returnVal = NO;
[[[secondPropertyObserver stub] andReturnValue:OCMOCK_VALUE(returnVal)] isKindOfClass:[OCMArg any]];

[[secondPropertyObserver expect] observeValueForKeyPath:@"secondProperty"
                                               ofObject:objectToObserve
                                                 change:OCMOCK_ANY
                                                context:[OCMArg anyPointer]];

[objectToObserve addObserver:secondPropertyObserver
                  forKeyPath:@"secondProperty"
                     options:NSKeyValueObservingOptionNew
                     context:NULL];

// Do something to modify objectToObserve's firstProperty    

[secondPropertyObserver verify];

[objectToObserve removeObserver:secondPropertyObserver
                     forKeyPath:@"secondProperty"];
2 голосов
/ 07 марта 2012

Если есть какой-то результат для secondProperty получения уведомления, которое вы можете проверить, я бы просто установил для него фактический объект и впоследствии проверил состояние.Если это невозможно, вы можете создать частичный макет secondProperty и ожидать обратного вызова KVO:

id observedObject = // ...initialize observed class
observedObject.secondProperty = // initialize secondProperty
id mockSecondProperty = [OCMockObject partialMockForObject:observedObject.secondProperty];
[[mockSecondProperty expect] observeValueForKeyPath:@"firstProperty" ofObject:observedObject change:OCMOCK_ANY context:OCMOCK_ANY];

observedObject.firstProperty = // modify firstProperty

[mockSecondProperty verify];
1 голос
/ 27 июля 2012

Только для тех, кто заинтересован в этом: вот решение без OCMock.

Я просто связываю значение, которое я ожидаю изменить, со свойством test моего класса модульного тестирования.

InВ следующем примере я хочу обновить свойство numberOfPages и запускать уведомления KVO при изменении массива pages.

Я объявляю свойство boundInteger в моем DocumentTests классе:

@interface DocumentTests : SenTestCase 
@property (assign) int boundInteger;
@end

Затем я реализую свой тестовый пример следующим образом:

- (void)testNumberOfPages
{
    Document *doc = [[Document alloc] init];

    [self bind:@"boundInteger" toObject:doc withKeyPath:@"numberOfPages" options:nil];

    doc.pages = arrayOfThreePagesIHaveBuildSomewhereElse;

    STAssertEquals(self.boundInteger, 3, @"Wrong number of pages.");
    STAssertEquals(doc.numberOfPages, 3, @"Wrong number of pages.");

    [self unbind:@"boundInteger"];
}

Работает нормально дляя :) 1017 *

...