2013-12-16 5 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이 복사본은 속성이 copy이기 때문에 생성자에서 불필요합니다. 하지만 고마워. –

+2

블록을 속성을 백업하는 ivar에 할당하기 때문에 생성자 *의'[copy]'가 * 필요합니다. 속성 선언의'(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"); 

불필요한 조롱을 많이하고 있어요. 나는 검증 대상에 특히 테스트 설정을 집중 시키려고합니다. 예를 들어, 부작용과 스터 빙을 최소화하기 위해 nil 매개 변수를 보내는 것과 같습니다.

- (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

조언을 주셔서 감사합니다. 이 테스트는 블록이 호출되었는지 만 확인하면 ok입니다 (지금이 테스트를 추가했습니다, 감사합니다.)). 그러나 내 테스트는 블록의 인수가 올바르게 있는지 테스트합니다. 만약 어떤 셀을 얻고 싶다면,'dequeueReusableCellWithIdentifier : indexPath :'가'tableView : cellForRowAtIndexPath :'에서 호출 되었기 때문에, 어떤 셀을 반환하는 tableView 모의 (mock)을 설정해야합니다. –