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

Почему в моих модульных тестах мой объект имеет слабое свойство делегирования?

У меня довольно простая настройка для этого unit test. У меня есть класс, обладающий свойством делегирования:

@interface MyClass : NSObject
...
@property (nonatomic, weak) id<MyDelegateProtocol> connectionDelegate;
...
@end

и я установил делегат в свой тест:

- (void)testMyMethod_WithDelegate {
  id delegate = mockDelegateHelper(); // uses OCMock to create a mock object
  [[delegate expect] someMethod];
  myClassIvar.connectionDelegate = delegate;
  [myClass someOtherMethod];
  STAssertNoThrow([delegate verify], @"should have called someMethod on delegate.");
}

Но делегат фактически не установлен в строке 3 моего unit test, поэтому #someMethod никогда не вызывается. Когда я меняю его на

myClassIvar.connectionDelegate = delegate;
STAssertNotNil(myClassIvar.connectionDelegate, @"delegate should not be nil");

он не работает. Я использую ARC, поэтому моя догадка заключалась в том, что слабая собственность была освобождена. Разумеется, переключение на strong делает проход STAssertNotNil. Но я не хочу делать это с делегатом, и я не понимаю, почему это имеет значение здесь. Из того, что я прочитал, все локальные ссылки в ARC strong, и STAssertNotNil(delegate) проходит. Почему мое слабое свойство делегата nil, когда тот же объект в локальной переменной не является?

4b9b3361

Ответ 1

Это ошибка во время выполнения iOS. Следующее обсуждение более подробно. В двух словах, среда выполнения iOS ARC не может обрабатывать слабые ссылки на прокси. Время выполнения OSX может.

http://www.mulle-kybernetik.com/forum/viewtopic.php?f=4&t=252

Насколько я понимаю из обсуждения, отчет об ошибке был подан в Apple. Если у кого-то есть разумная идея обходного пути...

Ответ 2

Я действительно не знаю, что здесь происходит, но OCMock возвращает автореализованный NSProxy -отступник из метода mockForProtocol:, который Я думаю, это правильно. Возможно, у ARC есть проблемы с NSProxies? Во всяком случае, я преодолел эту проблему, объявив переменную __weak:

- (void)testMyMethod_WithDelegate {
  // maybe you'll also need this modifier inside the helper
  __weak id delegate = mockDelegateHelper(); 
  ...

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

Ответ 3

Обходной путь заключается в использовании Partial Mocks.

@interface TestMyDelegateProtocolDelegate : NSObject <MyDelegateProtocol>
@end

@implementation TestMyDelegateProtocolDelegate
- (void)someMethod {}
@end


@implementation SomeTest {
- (void)testMyMethod_WithDelegate {
  id<MyDelegateProtocol> delegate = [[TestMyDelegateProtocolDelegate] alloc] init];
  id delegateMock = [OCMockObject partialMockForObject:delegate]
  [[[delegateMock expect] someMethod]
  myClassIvar.connectionDelegate = delegate;
  [myClass someOtherMethod];
  STAssertNoThrow([delegate verify], @"should have called someMethod on delegate.");
}
@end

Ответ 4

Я не эксперт ARC, но думаю, что mockDelegateHelper() возвращает слабый объект. В результате delegate равен нулю перед выполнением второй строки кода. Я бы рискнул предположить, что либо mockDelegateHelper() является виновником, либо OCMock мешает тому, как он манипулирует и создает объекты.