2012-02-18 62 views
0

我一直在使用Objective C和Cocoa/iOS並像測試者一樣進行測試。 (Definition在ObjectiveC中做BDD時嘲笑合作者的最好方法是什麼?

  • 我想嘲笑使用OCMock的對象的協作者。
  • 有目標C做這兩種方式我所知道的:

    1. 依賴注入
    2. 設置內部狀態 - 無論是通過訪問器或setValue方法:forKey:

我應該使用哪一種?

我不喜歡這些。但我必須使用一個...除非我還沒有其他選項。

1.依賴注入

這雜波我的代碼,尤其是當SUT有2/3的合作者。如果SUT需要通過1/2參數,事情看起來確實非常混亂。

我明白,遠遠超過3,有太多的依賴關係,對象應該分成其他部分...但即使有2個依賴關係和1個參數,它仍然是醜陋的罪過。

2.設置內部狀態

這打亂了類的內部 - 我覺得這是一個很大的禁忌測試。

訪問者絕對不在 - 他們暴露的數據是沒有人應該知道的。 我可以使用setValue:forKey:...但這感覺像一個可怕的黑客。

這也意味着我必須初始化SUT,然後換出真正的模擬合作伙伴,然後運行測試中的方法,這感覺很混亂。

我的問題

什麼是做BDD時,模擬出的目標C合作者的最好方法是什麼?

代碼

懲戒使用的setValue:forKey:

@interface JGCompositeCommand : JGCommand <JGCompositeCommandProtocol> { 
    NSMutableArray *commands; 
    JGCommandFinderFactory *commandFinderFactory; 
} 

-(id <JGCommandProtocol>)initWithName:(NSString *)name_ recoverer:(id <JGCommandRecoveryProtocol>)recoverer_ executor:(id <JGCommandExecutorProtocol>)executor_; 

@end 

@implementation JGCompositeCommand 

-(id)initWithName:(NSString *)name_ recoverer:(id)recoverer_ { 
    self = [super initWithName:name_ recoverer:recoverer_]; 
    if (self) { 
     commands = [NSMutableArray array]; 
     commandFinderFactory = [[JGCommandFinderFactory alloc] init]; 
    } 
    return self; 
} 

-(id <JGCommandProtocol>)commandWithName:(NSString *)name_ { 
    return [[commandFinderFactory commandFinderWithCommandName:name_ andCommands:commands] findCommandWithName]; 
} 

@end 

@interface JGCommandTestCase : SenTestCase { 
    JGCompositeCommand *compositeCommand; 
    OCMockObject *commandFinderFactoryMock; 
} 

@end 

@implementation JGCommandTestCase 

-(void)setUp { 
    [super setUp]; 
    compositeCommand = [[JGCompositeCommand alloc] initWithName:@"" recoverer:nil]; 
    commandFinderFactoryMock = [OCMockObject mockForClass:[JGCommandFinderFactory class]]; 
    // Hack alert! Ugh. 
    [compositeCommand setValue:commandFinderFactoryMock forKey:@"commandFinderFactory"]; 
} 

-(void)testGivenCommandNotFoundShouldThrow { 
    // ** Setup ** 
    [[[commandFinderFactoryMock expect] andReturn:...] commandFinderWithCommandName:... andCommands:...]; 

    // ** Execute ** 
    [compositeCommand commandWithName:@"Blah"]; 

    // ** Asserts ** 
    [commandFinderFactoryMock verify]; 
} 

@end 

回答

0

我們的就是讓合作者單身,並提供了一種方法,在注入一個模擬實例降落的方法測試時間:

static JGCommandFinder *sharedFinder = nil; 

+(JGCommandFinder *)sharedFinder { 
    if (sharedFinder == nil) sharedFinder = [[JGCommandFinder alloc] init]; 
    return sharedFinder; 
} 

+(void)setSharedFinder:(JGCommandFinder *)instance { 
    sharedFinder = instance; 
} 

這是非常靈活的,因爲你可以使用實際的目標t,即使已經初始化了一個真正的對象,也要注入一個模擬對象,並通過將實例設置爲nil來重置它以使用真實對象。

+0

我很困惑 - 當然,這意味着每一位合作者都是單身?這肯定意味着大多數班級都變成了單身人士。我認爲一個單身人士是一個反模式。 – 2012-02-19 10:17:08

+0

我想這取決於你的意思是「每一位合作者」。當然還有其他的獲取和配置對象的模式。您選擇的取決於您的需求。例如,筆尖是依賴注入的一種形式,所以在筆尖中配置的任何東西都可以在測試時注入。至於單身人士作爲反模式,我認爲主要的批評是他們很難作爲協作者測試,這種方法試圖解決這個問題。看看http://blog.securemacprogramming.com/2011/02/on-singletons/。 – 2012-02-20 18:05:33