OCUnit тестирование NSNotification доставки - PullRequest
12 голосов
/ 09 июня 2009

Для игры, которую я разрабатываю, у меня есть несколько классов моделей, которые вызывают уведомления при изменении их состояния. Затем представление подписывается на эти уведомления и может реагировать на них.

Я делаю свои юнит-тесты для модели с OCUnit и хочу утверждать, что ожидаемые уведомления были опубликованы. Для этого я делаю что-то вроде этого:

- (void)testSomething {
    [[NSNotificationCenter defaultCenter] addObserver:notifications selector:@selector(addObject:) name:kNotificationMoved object:board];

    Board *board = [[Board alloc] init];
    Tile *tile = [Tile newTile];

    [board addTile:tile];

    [board move:tile];

    STAssertEquals((NSUInteger)1, [notifications count], nil);
    // Assert the contents of the userInfo as well here

    [board release];
}

Идея состоит в том, что NSNotificationCenter добавит уведомления к NSMutableArray, вызвав его метод addObject:.

Однако, когда я запускаю его, я вижу, что addObject: отправляется другому объекту (не моему NSMutableArray), в результате чего OCUnit перестает работать. Однако, если я закомментирую некоторый код (например, вызовы release или добавлю новый модульный тест), все начнет работать, как и ожидалось.

Я предполагаю, что это связано с проблемой синхронизации, или NSNotificationCenter каким-то образом полагается на цикл выполнения.

Есть ли какие-либо рекомендации для проверки этого? Я знаю, что мог бы добавить установщик в Board и ввести свой собственный NSNotificationCenter, но я ищу более быстрый способ сделать это (возможно, какой-то трюк, как динамически заменить NSNotificationCenter).

Ответы [ 3 ]

5 голосов
/ 11 июня 2009

Нашел проблему. При тестировании уведомлений вам необходимо удалить наблюдателя после того, как вы его протестировали. Рабочий код:

- (void)testSomething {
    [[NSNotificationCenter defaultCenter] addObserver:notifications selector:@selector(addObject:) name:kNotificationMoved object:board];

    Board *board = [[Board alloc] init];
    Tile *tile = [Tile newTile];

    [board addTile:tile];

    [board move:tile];

    STAssertEquals((NSUInteger)1, [notifications count], nil);
    // Assert the contents of the userInfo as well here

    [board release];
    [[NSNotificationCenter defaultCenter] removeObserver:notifications name:kNotificationMoved object:board];
}

Если вам не удастся удалить наблюдателя, после запуска теста и освобождения некоторых локальных переменных центр уведомлений попытается уведомить эти старые объекты при запуске любого последующего теста, который вызывает такое же уведомление.

0 голосов
/ 11 июня 2009

Если вы подозреваете, что в ваших тестах есть проблемы с синхронизацией - вы можете рассмотреть возможность внедрения собственного механизма уведомлений в объект Board (который, вероятно, является просто оболочкой существующей версии Apple).

То есть:

Board *board = [[Board alloc] initWithNotifier: someOtherNotifierConformingToAProtocol];

Предположительно ваш объект на форуме отправляет какое-то уведомление - вы будете использовать введенное уведомление в этом коде:

-(void) someBoardMethod {

  // ....

  // Send your notification indirectly through your object
  [myNotifier pushUpdateNotification: myAttribute];
}

В вашем тесте - теперь у вас есть уровень косвенности, который вы можете использовать для тестирования, чтобы вы могли реализовать тестовый класс, соответствующий вашему AProtocol - и, возможно, подсчитать количество вызовов pushUpdateNotification :. В своем реальном коде вы инкапсулируете код, который вы, вероятно, уже имеете в Board, который отправляет уведомления.

Это, конечно, классический пример, где полезны MockObjects - и есть OCMock, который позволяет вам делать это без необходимости иметь тестовый класс для подсчета (см .: http://www.mulle -kybernetik.com / программное обеспечение / OCMock / )

ваш тест, вероятно, будет иметь строку вроде:

[[myMockNotifer expect] pushUpdateNotification: someAttribute]; 

В качестве альтернативы вы можете рассмотреть возможность использования делегата вместо уведомлений. Здесь есть хороший набор слайдов: http://www.slideshare.net/360conferences/nsnotificationcenter-vs-appdelegate.

0 голосов
/ 09 июня 2009

Нет проблем синхронизации или проблем, связанных с циклом выполнения, поскольку все в вашем коде не является параллельным и должно выполняться немедленно. NSNotificationCenter только откладывает доставку уведомлений, если вы используете NSNotificationQueue.

Я думаю, что все правильно во фрагменте, который вы разместили. Возможно, есть проблема с изменяемым массивом «уведомлений». Вы инициировали и сохранили это правильно? Попробуйте добавить какой-либо объект вручную, а не использовать трюк с уведомлением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...