2009-07-06 6 views
6

Мне интересно, как это проверить. У меня есть метод, который принимает параметр и на основе некоторых свойств этого параметра создает другой объект и работает на нем. Код выглядит примерно так:Как я могу тестировать объект внутри метода в Objective-C?

- (void) navigate:(NavContext *)context { 
    Destination * dest = [[Destination alloc] initWithContext:context]; 
    if (context.isValid) { 
    [dest doSomething]; 
    } else { 
    // something else 
    } 
    [dest release]; 
} 

Что я хочу, чтобы проверить, что если context.isValid верно, что йоЗотеЬЫпд вызывается Dest, но я не знаю, как проверить, что (или, если это даже возможно) с использованием OCMock или любых других традиционных методов тестирования, поскольку этот объект полностью создается в рамках метода. Я иду об этом неправильно?

ответ

5

Вы могли бы используйте OCMock, но вам придется изменить код, чтобы взять объект Destination или использовать объект singleton, который вы могли бы заменить своим макетным объектом.

Чистейший способ сделать это, вероятно, будет реализовать метод

-(void) navigate:(NavContext *)context destination:(Destination *)dest; 

. Изменение реализации -(void) navigate:(NavContext *)context к следующему:

- (void) navigate:(NavContext *)context { 
    Destination * dest = [[Destination alloc] initWithContext:context]; 
    [self navigate:context destination:dest]; 
    [dest release]; 
} 

Это позволит тесты, чтобы вызвать метод с дополнительным параметром непосредственно. (На других языках вы могли бы реализовать это просто, указав значение по умолчанию для целевого параметра, но Objective-C не поддерживает параметры по умолчанию.)

+0

Это, кажется, самый простой ответ, но кажется немного беспорядочным, чтобы объект назначения жил в нескольких областях, когда это действительно не нужно в другом месте, просто для поддержки простого тестирования. Я думаю, что компромиссы должны быть сделаны где-то :) – Kevlar

+0

Да; если вы хотите, чтобы иметь возможность заменить отдельный объект Destination, тогда вам нужно каким-то образом передать его. Это самый чистый способ, который я знаю об этом. –

0

Это возможно, используя такие интересные методы, как method swizzling, но, вероятно, это происходит неправильно. Если нет абсолютно никакого способа наблюдать последствия вызова doSomething из модульного теста, не факт, что он вызывает doSomething деталь реализации?

(Если вы должны были сделать этот тест, один из способов для достижения ваших целей будут заменить doSomething метод Destination с одним, который оповещает ваше модульное тестирование, а затем переходит на вызов к doSomething.)

+0

Я полагаю, это не имеет большого значения, если я не проверю, вызван ли метод.Я все еще немного новичок в тестировании TDD/unit, поэтому я еще не знаю всех лучших практик :) – Kevlar

1

Что я хочу проверить, так это, если context.isValid true , that doSomething вызывается в dest

Думаю, вы можете протестировать здесь не то. Вы можете смело предположить (я надеюсь), что логические операторы корректно работают в ObjC. Не хотите ли вы протестировать объект Context? Если context.isValid, тогда вам гарантируется выполнение ветки [dest doSomething].

+0

Я издеваюсь над объектом контекста, так как в этом тесте мне все равно, как он создается, я забочусь о том, что метод делает правильную вещь на основе свойств контекста. – Kevlar

+0

Кроме того, если реализация навигации: когда-либо изменится, он все равно может захотеть проверить, что вызвано doSomething. –

+0

Ах, честно говоря, я был немного недалек. Мне нравится подход Б. Дж. Гомера. – EightyEight

0

Мне нравится использовать заводские методы в этой ситуации.

@interface Destination(Factory) 
+ (Destination *)destinationWithContext:(NavContext *)context; 
@end 

@implementation Destination(Factory) 
+ (Destination *)destinationWithContext:(NavContext *)context 
{ 
    return [[Destination alloc] initWithContext:context]; 
} 
@end 

Я тогда сделать FakeClass:

#import "Destination+Factory.h" 

@interface FakeDestination : Destination 
+ (id)sharedInstance; 
+ (void)setSharedInstance:(id)sharedInstance; 
// Note! Instance method! 
- (Destination *)destinationWithContext:(NavContext *)context; 
@end 

@implementation FakeDestination 
+ (id)sharedInstance 
{ 
    static id _sharedInstance = nil; 
    if (!_sharedInstance) 
    { 
     _sharedInstance = [[FakeDestination alloc] init]; 
    } 
    return _sharedInstance; 
} 
+ (void)setSharedInstance:(id)sharedInstance 
{ 
    _sharedInstance = sharedInstance; 
} 
// Overrides 
+ (Destination *)destinationWithContext:(NavContext *)context { [FakeDestination.sharedInstance destinationWithContext:context]; } 
// Instance 
- (Destination *)destinationWithContext:(NavContext *)context { return nil; } 
@end 

После того, как вы установили это, вы просто должны swizzle the class methods для + (Destination *)destinationWithContext:(NavContext *)context;

Теперь вы установите на:

id destinationMock = [OCMock mockForClass:FakeDestination.class]; 
// do the swizzle 
[FakeDestination setSharedInstance:destinationMock]; 
[[destinationMock expect] doSomething]; 
// Call your method 
[destinationMock verify]; 

Это отличная кодировка впереди, но она очень многоразовая.

 Смежные вопросы

  • Нет связанных вопросов^_^