Как изменить значение возвращаемого значения OCMock-заглушки?
Кажется, что в первый раз, когда я добавляю andReturnValue на заглушке OCMock, это возвращаемое значение устанавливается в виде камня. Например:
id physics = [OCMockObject niceMockForClass:[DynamicPhysicsComponent class]
Entity *testEntity = [Entity entityWithPhysicsComponent:physics];
CGPoint velocity1 = CGPointMake(100, 100);
CGPoint velocity2 = CGPointZero;
[[[physics stub] andReturnValue:OCMOCK_VALUE(velocity1)] getCurrentVelocity];
[testEntity update:0.1];
[[[physics stub] andReturnValue:OCMOCK_VALUE(velocity2)] getCurrentVelocity];
[testEntity update:0.1];
Описанный метод вызывается в [updateEntity update]. Но каждый раз, когда закодированный метод возвращает значение velocity1, я предполагаю, что вторая попытка установить возвращаемое значение методов не выполняется.
Есть ли способ сделать это в OCMock?
Ответы
Ответ 1
Когда вы stub
метод, вы говорите, что он должен всегда функционировать определенным образом, независимо от того, сколько раз он вызывал. Самый простой способ исправить это - изменить stub
на expect
:
CGPoint velocity1 = CGPointMake(100, 100);
CGPoint velocity2 = CGPointZero;
[[[physics expect] andReturnValue:OCMOCK_VALUE(velocity1)] getCurrentVelocity];
[testEntity update:0.1];
[[[physics expect] andReturnValue:OCMOCK_VALUE(velocity2)] getCurrentVelocity];
[testEntity update:0.1];
В качестве альтернативы, если вам нужно stub
(например, если метод вообще не может быть вызван), вы можете просто повторно создать mock:
CGPoint velocity1 = CGPointMake(100, 100);
CGPoint velocity2 = CGPointZero;
[[[physics stub] andReturnValue:OCMOCK_VALUE(velocity1)] getCurrentVelocity];
[testEntity update:0.1];
[physics verify];
physics = [OCMockObject mockForClass:[Physics class]];
[[[physics stub] andReturnValue:OCMOCK_VALUE(velocity2)] getCurrentVelocity];
[testEntity update:0.1];
[physics verify];
Ответ 2
Фактически, когда вы stub
вы устанавливаете только возвращаемое значение в камне, если используете andReturn
или andReturnValue
. Вы можете использовать метод andDo
, чтобы изменить возвращаемое значение, когда захотите. Это улучшение по сравнению с expect
, где вам нужно знать, сколько раз будет вызван метод. Вот фрагмент кода, чтобы выполнить это:
__weak TestClass *weakSelf = self;
[[[physics stub] andDo:^(NSInvocation *invocation) {
NSValue *result = [NSValue valueWithCGPoint:weakSelf.currentVelocity];
[invocation setReturnValue:&result];
}] getCurrentVelocity];
Ответ 3
Хотя я думаю, что CipherCom имеет правильный ответ, я считаю, что предпочитаю создавать вспомогательный класс для возврата различных значений. У меня были проблемы с NSInvocation
в прошлом.
@interface TestHelper : NSObject
@property (nonatomic, assign) CGPoint velocity;
- (CGPoint)getCurrentVelocity;
@end
@implementation TestHelper
- (CGPoint)getCurrentVelocity
{
return self.velocity;
}
@end
Тогда в моем тестовом классе у меня была бы приватная переменная-член для TestHelper
и в методе setUp
, которую я сделал бы:
self.testHelper = [TestHelper new];
[[[physics stub] andCall:@selector(getCurrentVelocity) onObject:self.testHelper]
getCurrentVelocity];
Таким образом, в каждом из моих тестов я мог бы установить скорость на то, что я хочу для теста.
self.testHelper.velocity = CGPointMake(100, 200);
Ответ 4
Похоже, что ни andReturn
/andReturnValue
/andDo
не переустанавливается при вызове несколько раз. Мое обходное решение состояло в том, чтобы добавить свойство к тестовому классу и использовать его для контроля того, что должен вернуть издеваемый объект. Например, в случае свойства isAvailable
на издеваемом объекте мой код будет выглядеть так:
@interface MyTest: XCTestCase
@property BOOL stubbedIsAvailable;
@end
@implementation MyTest
- (void)setUp {
[OCMStub([myMockedObject isAvailable]) andDo:^(NSInvocation invocation) {
BOOL retVal = self.stubbedIsAvailable;
[invocation setReturnValue:&retVal];
}
}
- (void)testBehaviourWhenIsAvailable {
self.stubbedIsAvailable = YES;
// test the unit
}
- (void)testBehaviourWhenIsNotAvailable {
self.stubbedIsAvailable = NOT;
// test the unit
}