如何对一个UIViewController进行单元测试 - TDD/BDD

10

单元测试似乎一直是我难以理解的东西,但我可以看出它为什么很重要,并且如果你知道如何使用,它可以节省大量时间。我希望有人能给我指点方向。

我有以下的UIViewController

QBElectricityBaseVC.h

@interface QBElectricityBaseVC : QBStateVC

@property (nonatomic, strong) QBElectricityUsage *electricityUsage;
@property (nonatomic, assign) CGFloat tabBarHeight;

- (void)updateElectricityUsage;

@end

QBElectricityBaseVC.m

@implementation QBElectricityBaseVC

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.tabBarItem = [[UITabBarItem alloc] initWithTitle:NSLocalizedString(@"electricity_title", nil) image:nil tag:0];
    }
    return self;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.notificationCenter addObserver:self selector:@selector(updateElectricityUsage)
                                                 name:kUpdatedElectricityUsageKey object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [self.notificationCenter removeObserver:self];
}

- (void)updateElectricityUsage
{
    self.electricityUsage = [self.stateManager electricityUsage];
}

- (CGFloat)tabBarHeight
{
    return self.tabBarController.tabBar.frame.size.height;
}

@end

我应该测试什么?

  • 添加了对kUpdatedElectricityUsageKey的观察器
  • self.electricityUsage成为QBElectricityUsage的实例
  • 返回一个tabBarHeight
  • 删除了对kUpdatedElectricityUsageKey的观察器

我是否遗漏了任何应该测试的内容,或者测试了一些不必要的内容?

如何进行测试?

因此,我正在尝试使用SpectaExpexta进行测试。如果需要模拟任何内容,我将使用OCMockito

我真的不知道如何测试观察器是添加/删除的。我在Expexta文档中看到以下内容,但不确定它是否相关以及如何使用:

expect(^{ /* code */ }).to.notify(@"NotificationName"); passes if a given block of code generates an NSNotification named NotificationName.

expect(^{ /* code */ }).to.notify(notification); passes if a given block of code generates an NSNotification equal to the passed notification.

为了测试self.electricityUsage是否成为QBElectricityUsage的实例,我可以创建一个类别(category),其中有一个方法,仅假装通知被触发并调用updateElectricityUsage方法,但这是最好的方法吗?

至于tabBarHeight,我应该只测试它返回一个有效的CGFloat而不担心值是多少吗?


更新

我修改了我的viewWillAppear方法如下:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self addNotificationObservers];
}

- (void)addNotificationObservers
{
    [self.notificationCenter addObserver:self selector:@selector(updateElectricityUsage)
                                    name:kUpdatedElectricityUsageKey object:nil];
}

然后我创建了以下测试:

#import "Specs.h"

#import "QBElectricityBaseVC.h"
#import "ElectricityConstants.h"

SpecBegin(QBElectricityBaseVCSpec)

    describe(@"QBElectricityBaseVC", ^{
        __block QBElectricityBaseVC *electricityBaseVC;
        __block NSNotificationCenter *mockNotificationCenter;

        beforeEach(^{
            electricityBaseVC = [QBElectricityBaseVC new];
            mockNotificationCenter = mock([NSNotificationCenter class]);
            electricityBaseVC.notificationCenter = mockNotificationCenter;
        });

        afterEach(^{
            electricityBaseVC = nil;
            mockNotificationCenter = nil;
        });

        it(@"should have a notification observer for updated electricity usage", ^{
            [electricityBaseVC addNotificationObservers];
            [verify(mockNotificationCenter) addObserver:electricityBaseVC selector:@selector(updateElectricityUsage)
                                               name:kUpdatedElectricityUsageKey object:nil];
        });
    });

SpecEnd

现在该测试已经通过了,但是这是否是测试的正确/最佳方式呢?


我有完全相同的问题 - 希望有更多的文档。另外,为什么不使用XCTest? - diogocarmo
2个回答

4
您刚刚感受到了iOS ViewControllers的一个缺点:它们很难进行测试
  • ViewControllers混合了管理视图和模型的逻辑
    • 这导致了庞大的ViewControllers
    • 这违反了单一职责原则
      • 这使得代码不可重用

MVC的另一个大问题是它阻碍了开发人员编写单元测试。由于视图控制器混合了视图操作逻辑和业务逻辑,为了进行单元测试而将这些组件分离出来成为一项艰巨的任务。许多人为了…不测试任何东西而忽略了这个任务。

文章-来源

也许您应该考虑使用MVVM。这是一篇很好的文章,解释了iOS MVC和MVVM之间的区别。

使用MVVM的好处在于可以使用响应式Cocoa进行数据绑定。这里有一个教程,将解释iOS中使用MVVM和响应式编程的数据绑定。

2
我有两种实践方法来测试UIViewController的组件。
  1. MVVM - 使用MVVM模式,您可以在ViewModel类的单元测试中非常轻松地对视图内容进行单元测试。这也使您的ViewController逻辑非常简单,因此您不必编写太多UI测试来覆盖所有这些场景。
  2. KIF - 然后,对于UI测试,我使用KIF,因为它的测试actor可以帮助处理异步和视图加载延迟。使用KIF,我可以从我的代码中发布通知,然后我的测试将等待查看我的通知处理程序在视图中的效果。
通过这两个系统,我能够对几乎所有内容进行单元测试,然后非常容易地编写UI测试来覆盖最终部分。
另外,关于您的代码的快速说明:我不会在viewWillAppear中添加观察者,因为它会被调用多次。但是,由于通知合并,您可能不会因为重复调用处理程序而遇到问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接