Как установить ожидания по параметрам для смоделированных методов в киви
Используя OCMockito и OCHamcrest, я могу установить ожидания в отношении аргументов для смоделированных методов, таким образом:
[verify(aMockObject) doSomething:allOf(is(instanceOf([NSArray class])), hasCountOf(3U), nil)];
Кажется, не существует такого же простого способа сделать это с помощью киви. Можно захватить аргументы, используя шпиона, что-то вроде:
KWCaptureSpy *spy = [aMockObject captureArgument:@selector(doSomething:) atIndex:0];
NSArray *capturedArray = spy.argument;
А затем, чтобы проверить ожидания на захваченном объекте:
[[capturedArray should] haveCountOf:3U];
Есть ли менее неуклюжий способ сделать это на киви?
(Я знаю, что мог бы использовать здесь приспособления для подколенного сухожилия, но сейчас я изучаю, на что способен киви).
1 ответ
Один вариант, который я использовал, это stub:withBlock:
NSArray* capturedArray; // declare this as __block if needed
[aMockObject stub:@selector(doSomething:)
withBlock:^id(NSArray *params) {
capturedArray = params[0];
// this is necessary even if the doSomething method returns void
return nil;
}];
// exercise your object under test, then:
[[capturedArray should] haveCountOf:3U];
Это прекрасно работает, и я считаю, что это легче реализовать, чем шаблон шпиона. Но ваш вопрос заставил меня задуматься об ожиданиях с помощью шаблонов сообщений. Например:
[[[aMockObject should] receive] doSomething:myArray];
[[[aMockObject should] receive] doSomething:any()];
Первый пример подтвердит, что aMockObject
получил doSomething:
сообщение с аргументом, что isEqual:myArray
, Второй пример просто подтвердит, что doSomething:
был отправлен, без ожидания о массиве arugment. Было бы замечательно, если бы мы могли указать некоторый тип Matcher в шаблоне сообщения, чтобы выразить, что нам все равно, какой конкретный экземпляр массива отправляется в сообщении, просто что он имеет count
из 3.
Я не нашел примеров того, как это можно сделать, но похоже, что есть некоторые возможности. Чтобы проверить ожидание отправки сообщения, Kiwi использует KWMessagePattern
класс, а именно matchesInvocation:
а также argumentFiltersMatchInvocationArguments:
методы. Это проверяет три типа "фильтров аргументов":
- Литеральные значения объекта (такие как
myArray
в приведенном выше примере), которые сравниваются с фактическим значением, отправленным в сообщении с использованиемisEqual:
- Объект типа
KWAny
(такой какany()
макрос в примере выше), который будет соответствовать любому значению аргумента - Объекты, которые удовлетворяют
[KWGenericMatchEvaluator isGenericMatcher:argumentFilter]
Это означает, что объект реагирует наmatches:(id)obj
Таким образом, вы должны быть в состоянии использовать объекты, которые реализуют matches:
в ожиданиях шаблонов сообщений выполнять такие действия, как проверка длины массивов, отправляемых в тупые методы, не прибегая к шпионам или блокам. Вот очень простая реализация: ( доступно как Gist)
// A reusable class that satisfies isGenericMatcher:
@interface SOHaveCountOfGenericMatcher : NSObject
- (id)initWithCount:(NSUInteger)count;
- (BOOL)matches:(id)item; // this is what KWMessagePattern looks for
@property (readonly, nonatomic) NSUInteger count;
@end
@implementation SOHaveCountOfGenericMatcher
- (id)initWithCount:(NSUInteger)count
{
if (self = [super init]) {
_count = count;
}
return self;
}
- (BOOL)matches:(id)item
{
if (![item respondsToSelector:@selector(count)])
return NO;
return [item count] == self.count;
}
@end
// Your spec:
it(@"should receive an array with count 3", ^{
NSArray* testArray = @[@"a", @"b", @"c"];
id argWithCount3 = [[SOHaveCountOfGenericMatcher alloc] initWithCount:3];
id aMockObject = [SomeObj nullMock];
[[[aMockObject should] receive] doSomething:argWithCount3];
[aMockObject doSomething:testArray];
});
Было бы неплохо иметь возможность повторно использовать встроенные классы соответствия в Kiwi, но я пока не выяснил, как именно это сделать.