2013-12-16 67 views
0

我有UITableView它有代表和分離的數據源。 DataSource是NSObject - ArrayDataSource類的子類。我應該如何測試塊是否已經在這個類中引發?

#import <Foundation/Foundation.h> 

typedef void (^ConfigureCellBlock)(id cell, id object); 

@interface ArrayDataSource : NSObject <UITableViewDataSource> 

@property (nonatomic, readonly) NSArray *items; 
@property (nonatomic, readonly) NSString *cellIdentifier; 

- initWithItems:(NSArray *)items cellIdentifier:(NSString *)cellIdentifier configureCellBlock:(ConfigureCellBlock)block; 

- (void)setItems:(NSArray *)items; 
- (void)setCellIdentifier:(NSString *)cellIdentifier; 
- (void)setConfigureCellBlock:(ConfigureCellBlock)block; 

@end 



#import "ArrayDataSource.h" 

@interface ArrayDataSource() 
@property (nonatomic, copy) ConfigureCellBlock configureCellBlock; 
@end 

@implementation ArrayDataSource { 
    NSArray *_items; 
    NSString *_cellIdentifier; 
} 

#pragma mark - External 
- (id)initWithItems:(NSArray *)items cellIdentifier:(NSString *)cellIdentifier configureCellBlock:(ConfigureCellBlock)block { 
    self = [super init]; 
    if (self) { 
     _items = items; 
     _cellIdentifier = cellIdentifier; 
     _configureCellBlock = [block copy]; 
    } 
    return self; 
} 

- (void)setItems:(NSArray *)items { 
    _items = items; 
} 

- (void)setCellIdentifier:(NSString *)cellIdentifier { 
    _cellIdentifier = cellIdentifier; 
} 

- (void)setConfigureCellBlock:(ConfigureCellBlock)block { 
    _configureCellBlock = block; 
} 


#pragma mark - Private 
- (id)_itemAtIndexPath:(NSIndexPath *)indexPath { 
    return _items[indexPath.row]; 
} 

#pragma mark - UITableViewDataSource 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return _items.count; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    id cell = [tableView dequeueReusableCellWithIdentifier:_cellIdentifier forIndexPath:indexPath]; 
    id item = [self _itemAtIndexPath:indexPath]; 
    _configureCellBlock(cell, item); 
    return cell; 
} 


#pragma mark - Accessors 
- (NSArray *)items { 
    return _items; 
} 

- (NSString *)cellIdentifier { 
    return _cellIdentifier; 
} 

@end 

現在我要測試是否ConfigureCellBlocktableView:cellForRowAtIndexPath:被稱爲使用正確的參數。

我的測試現在看起來像下面但它不能正常工作,因爲它傳遞和塊並不是所有的調用。我的測試班是_tableView的代表。

#pragma mark - UITableView tests + Delegate + DataSource 
- (void)testDataSource { 
    UITableView *_tableView = [[UITableView alloc] init]; 
    _tableView.delegate = self; 

    [dataSource setItems:@[@"A"]]; 

    [dataSource setConfigureCellBlock:^(UITableViewCell *cell, NSString *object) { 
     XCTAssertEqualObjects(object, @"B", @""); 
    }]; 

    id mockDataSource = [OCMockObject partialMockForObject:dataSource]; 
    SEL selector = NSSelectorFromString(@"_itemAtIndexPath:"); 
    [[[mockDataSource stub] andReturn:dataSource.items[0]] methodForSelector:selector]; 

    _tableView.dataSource = mockDataSource; 

    UITableViewCell *cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; 
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; 

    id mockTableView = [OCMockObject partialMockForObject:_tableView]; 
    [[[mockTableView stub] andReturn:cell] dequeueReusableCellWithIdentifier:OCMOCK_ANY forIndexPath:indexPath]; 

    [mockTableView reloadData]; 
} 

我應該如何正確地測試它?

預先感謝您。

+0

這幾乎肯定不會解決您的問題,但不應該你的塊設置複製塊(就像你在構造函數中做的那樣)? – sammyd

+0

@sammyd這個副本在構造函數中是不必要的,因爲屬性是副本。但是,謝謝你。 –

+2

構造函數*中的'[copy]'是必需的,因爲您將塊分配給支持該屬性的ivar。屬性聲明中的'(copy)'指令只適用於合成的setter。然而,在你的代碼中,你已經覆蓋了setter,並且沒有使用copy,所以即使你使用'self.configureCellBlock = block',它也不會複製。 – sammyd

回答

0

我做到了。試驗測試,如果塊已經被稱爲-tableView:cellForRowAtIndexPath:

- (void)testDataSource { 
    /// Data Source 
    [dataSource setItems:@[@"A"]]; 
    [dataSource setConfigureCellBlock:^(UITableViewCell *cell, NSString *object) { 
#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Warc-retain-cycles" 
     XCTAssertNotNil(cell, @""); 
     XCTAssertEqualObjects(object, @"A", @""); 
#pragma clang diagnostic pop 
    }]; 

    /// Table View 
    id mockTableView = [OCMockObject niceMockForClass:[UITableView class]]; 
    [[[mockTableView stub] andReturn:dataSource] dataSource]; 

    UITableViewCell *cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; 
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; 

    [[[mockTableView stub] andReturn:cell] dequeueReusableCellWithIdentifier:OCMOCK_ANY forIndexPath:OCMOCK_ANY]; 

    [[mockTableView dataSource] tableView:mockTableView cellForRowAtIndexPath:indexPath]; 
} 

我用#pragma和忽略了塊警示關於保留週期。我不知道我該如何在沒有保留週期的情況下放置斷言。

謝謝@sammyd和@Sebastian。

3

我通過把一個標誌在塊中測試這種事情:

__block BOOL invoked = NO; 
[dataSource setConfigureCellBlock:^(UITableViewCell *cell, NSString *object) { 
    invoked = YES; 
}]; 

// do something to invoke block... 
XCTAssertTrue(invoked, @"configure cell block was not invoked"); 

在我看來,你做了很多不必要的嘲諷。我試着將測試設置專注於正在驗證的內容,例如發送零參數以最大限度地減少副作用和殘留。這是整個測試,因爲我會寫它:

- (void)testDataSource { 
    [dataSource setItems:@[@"A"]]; 

    __block BOOL invoked = NO; 
    [dataSource setConfigureCellBlock:^(UITableViewCell *cell, NSString *object) { 
     XCTAssertEqualObjects(object, @"A", @"got the wrong item"); 
     invoked = YES; 
    }]; 

    [dataSource tableView:nil cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 
    XCTAssertTrue(invoked, @"configure cell block was not invoked"); 
} 
+0

感謝您的建議。你說得對,如果這個測試只會檢查block是否被調用,那就沒關係(我現在增加了這個測試,謝謝;))。但我的測試也測試塊中的參數是否正確。如果我想獲得一些單元格,我需要配置tableView的模擬,它返回給我一些單元格,因爲在'tableView:cellForRowAtIndexPath:'中調用了'dequeueReusableCellWithIdentifier:indexPath:'。 –

相關問題