0

我正在尝试模拟和测试 UITableViewCells 以确保我的 configureCell:forIndexPath 工作正常,但我无法使用 isKindOfClass 使其工作,而只能使用符合 ToProtocol。这将要求我所有的 uitableviewcells 都有自己的协议,并且似乎不需要。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
  FeedObj *item = [_feedElements objectAtIndex:indexPath.row];
  if( item.obj_type == FeedObjTypeFriendAdd ) {
    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyTableViewCellIdentifier forIndexPath:indexPath];
    [self configureCell:cell forIndexPath:indexPath]
    return cell;
  } else if( item.obj_type = FeedObjTypeSomeOtherType ) {
    // do another cell
  }
}

- (void)configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath 
{
  // only enters conditional in test if I do [cell conformsToProtocol:@protocol(SomeIndividualProtocolForEachTableViewcell)]                  
  if( [cell isKindOfClass:[MyTableViewCell class]] ) {
    // do the configuring
    FeedObj *item = [_streamElements objectAtIndex:indexPath.row];

    NSString *firstName = [item.obj_data objectForKey:@"first_name"];
    NSString *lastName = [item.obj_data objectForKey:@"last_name"];
    NSString *name = [NSString stringWithFormat:@"%@ %@.", firstName, [lastName substringToIndex:1]];
    NSString *text = [NSString stringWithFormat:@"%@ has joined", name];

    [((MyTableViewCell *)cell).messageLabel setText:text];

  } else if( [cell isKindOfClass[SomeOtherTableView class]] ) {
    // do other config
  }
}


    @implementation SampleTests
    - (void)setUp
    {
        _controller = [[MySampleViewController alloc] init];
        _tableViewMock = [OCMockObject niceMockForClass:[UITableView class]];
        [_tableViewMock registerNib:[UINib nibWithNibName:@"MyTableViewCell" bundle:nil] forCellReuseIdentifier:MyTableViewCellIdentifier];
    }

    - (void)testFriendAddCell
    {
        FeedObj *friendAdd = [[FeedObj alloc] init];
        friendAdd.obj_type = FeedObjTypeFriendAdd;
        friendAdd.obj_data = [NSMutableDictionary dictionaryWithDictionary:@{ @"first_name" : @"firstname", @"last_name" : @"lastname" }];
        _mockStreamElements = [NSMutableArray arrayWithObject:friendAdd];
        [_controller setValue:_mockStreamElements forKey:@"_feedElements"];

        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
        [[[_tableViewMock expect] andReturn:[[[NSBundle mainBundle] loadNibNamed:@"MyTableViewCell" owner:self options:nil] lastObject]] dequeueReusableCellWithIdentifier:MyTableViewCellIdentifier forIndexPath:indexPath];

        MyTableViewCell *cell = (MyTableViewCell *)[_controller tableView:_tableViewMock cellForRowAtIndexPath:indexPath];
        STAssertNotNil( cell, @"should not be nil" );
        STAssertTrue( [cell.messageLabel.text isEqualToString:@"firstname l. has joined"], @"should be equal" );
        [_tableViewMock verify];
    }
    @end

我也尝试过使用 mockCell 期望做 [[[mockCell stub] andReturnValue:OCMOCK_VALUE((BOOL) {YES})] isKindOfClass:[MyTableViewCell class]]] 并且它也不起作用。像这样:

id mockCell = [OCMockObject partialMockForObject:[[[NSBundle mainBundle] loadNibNamed:@"MyTableViewCell" owner:self options:nil] lastObject]];
[[[mockCell stub] andReturnValue:OCMOCK_VALUE((BOOL) {YES})] isKindOfClass:[OCMConstraint isKindOfClass:[MyTableViewCell class]]];
[[[_tableViewMock expect] andReturn:mockCell] dequeueReusableCellWithIdentifier:MyTableViewCellIdentifier forIndexPath:indexPath];

我什至尝试使用http://blog.carbonfive.com/2009/02/17/custom-constraints-for-ocmock/中列出的 OCMConstraint 。

无论如何要这样做还是我必须为每个 tableviewcell 使用协议?提前致谢

4

1 回答 1

1

我强烈建议您重新考虑如何构建此实现。对于初学者来说,视图控制器非常擅长管理视图,但管理模型数据并不是它的用途。它有利于将模型数据传递给它管理的视图,因此考虑到这一点,让我们像这样构建它。

让我们从引入一个新类开始,称为FeedController. 该控制器的工作是介于您的 VC 和支持此屏幕的模型数据之间。让我们假设这个公共接口:

@interface FeedController : NSObject
- (instancetype)initWithFeedArray:(NSArray *)array;
- (NSString *)firstNameAtIndexPath:(NSIndexPath *)path;
- (NSString *)lastNameAtIndexPath:(NSIndexPath *)path;
- (NSString *)fullNameAtIndexPath:(NSIndexPath *)path;
// This should probably have a better name
- (NSString *)textAtIndexPath:(NSIndexPath *)path;
@end

我不打算实现这些方法,但它们看起来与您期望的完全一样。初始化程序将复制传递给它的数组,将其存储在 ivar 中,而其他方法将从特定索引处的数组中取出一条信息,并应用您必须的任何自定义转换(例如将名字和姓氏组合到获取全名)。这里的主要目标是传输数据,而不是操纵它。您尝试在视图控制器中操作这些数据的那一刻,就是您回到原点,明智地进行测试的那一刻。

您的目标configureCell:forIndexPath:现在只是从FeedController类中传输数据,这对测试非常简单。无需设置响应者链、模拟对象或任何东西。只需提供一些夹具数据就可以了。

您仍在测试组成您的部分,configureCell:forIndexPath:但不再直接测试该方法。如果您想确保正确填充视图,那很好,您应该这样做。但是,您会以不同的方式执行此操作,这不是单元测试的工作。拉出UIAutomation或您最喜欢的 UI 测试框架,并测试您的 UI。使用单元测试FeedController来测试您的数据和转换。

我希望这有帮助。

于 2013-09-30T21:23:12.610 回答