2015-07-06 4 views
2

Иногда я получаю EXC_BAD_ACCESS при выполнении этого теста:Случайные вылеты с OCMock

- (void)testThatItDoesNotSaveLoadedImageIfNotIntializedWithModifiesFileCacheOption { 

    id cacheMock = OCMClassMock([BEImageCache class]); 
    id imageLoaderMock = OCMClassMock([BEImageLoader class]); 

    BETwoLayerCacheImageProvider *imageProvider = [[BETwoLayerCacheImageProvider alloc] initWithCache:cacheMock imageLoader:imageLoaderMock options:0 scale:2.f]; 
    UIImage *expectedImage = [UIImage new]; 
    [[cacheMock reject] saveImageAtURLInFileCache:[OCMArg any] forURL:[OCMArg any]]; 
    [[cacheMock reject] saveImageInFileCache:[OCMArg any] forURL:[OCMArg any]]; 

    __block NSURL *URL = [NSURL fileURLWithPath:@"/file:///fixtureURL"]; 
    __block typeof(self) welf = self; 
    OCMStub([imageLoaderMock downloadImageWithURL:[OCMArg any] completionCallback:[OCMArg any]]).andDo(^(NSInvocation *invocation) { 

     ///[invocation retainArguments]; 
     void (^callback)(UIImage *image, NSError *error, NSString *imageURL, NSURL *temporaryImageURL) = nil; 
     [invocation getArgument:&callback atIndex:3]; 
     callback(expectedImage, nil, welf.smallImageURL, URL); 
    }); 

    [imageProvider fetchImageForImageData:self.imageData size:self.smallImageSize withCompletionCallback:^(UIImage *anImage, id<BEImageAware> imageData, CGSize requestedSize) { 

     OCMVerifyAll(cacheMock); 
    }]; 
} 

EXC_BAD_ACCESS происходит здесь:

- (void)forwardInvocationForClassObject:(NSInvocation *)anInvocation 
{ 
    // in here "self" is a reference to the real class, not the mock 
    OCClassMockObject *mock = OCMGetAssociatedMockForClass((Class) self, YES); 
    if(mock == nil) 
    { 
     [NSException raise:NSInternalInconsistencyException format:@"No mock for class %@", NSStringFromClass((Class)self)]; 
    } 
    if([mock handleInvocation:anInvocation] == NO) 
    { 
     [anInvocation setSelector:OCMAliasForOriginalSelector([anInvocation selector])]; 
     [anInvocation invoke]; /// EXC_BAD_ACCESS 
    } 
} 

Странная вещь в том, что она никогда не падает, когда я бегу только этот один тест , но я терпит крах почти всегда, когда я настраиваю все свои тесты (с cmd + u).

Я попытался добавить [invocation retainArguments], это не помогло, и я думаю, не должно быть здесь. У кого-нибудь были такие проблемы?

+1

Что-нибудь интересное в трассировке стека неудачи? Если вы выполняете все свои тесты, кроме этого, постоянно ли они проходят? –

ответ

2

Учитывая, что это только неудача, когда тесты выполняются вместе, возможно, что один из макет объектов для класса, который вы тестируете, остается после определенного теста. Например, скажем, ваш cacheMock укутывает метод в тесте (позволяет называть его testOne).

После запуска testOne, если вы не вызываете явным образом [self.cacheMock stopMocking] в конце этого файла, заглушка все еще будет включена для следующего тестового примера, что может привести к сбоям при попытке захвата аргументов посредством вызова.

Другими словами, убедитесь, что все ваши mocks остановлены в каждом тестовом случае или, по крайней мере, те, которые вы можете высмеять снова. Я предполагаю, что либо cacheMock, либо imageLoaderMock вызывают проблему.

+1

Хотя я должен использовать 'stopMocking' только для частичных mocks и при методах класса stubbing. Есть ли причина, по которой 'stopMocking' также необходим для локальных переменных? (Я обновил свой вопрос, чтобы было ясно, что оба 'cacheMock' и' imageLoaderMock' не являются частичными mocks). И я попробовал добавить вызовы 'stopMocking', это не помогло, к сожалению, – dariaa

0

Я испытываю те же проблемы с OCMock 3.

Я заметил авария была в методе stopMocking. Согласно документам OCMock «Макет автоматически вызывает stopMocking во время своего собственного освобождения». (http://ocmock.org/reference/))

В моем коде некоторые методы выполняют другие задачи в фоновом режиме (с использованием GCD), которые могут продолжаться и после завершения теста. Поэтому было , пытаясь получить доступ к макету, который уже был освобожден и, следовательно, к сбою. Для того, чтобы задачи, которые будут завершены, я добавил таймер в tearDown метод как это:

[NSThread sleepForTimeInterval:0.05]; 

Это даст время для задания выполнить перед издеваться освобождается.

Надеюсь, это полезно!