【问题标题】:How to unit test a UIViewController - TDD/BDD如何对 UIViewController 进行单元测试 - TDD/BDD
【发布时间】:2014-11-27 14:13:43
【问题描述】:

单元测试只是我似乎永远无法理解的事情,但我可以看到它为什么重要并且可以节省大量时间(如果您知道自己在做什么)。我希望有人能指出我正确的方向。

我有以下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 的实例,我可以创建一个类别,该类别具有一个方法,该方法只是假装已触发通知并调用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?

标签: ios unit-testing tdd bdd expecta


【解决方案1】:

您刚刚感受到 iOS ViewController 的一大弊端:它们的可测试性很差

  • ViewControllers 混合管理视图和模型的逻辑

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

Article - source

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

使用 MVVM 的好处是您可以通过 Reactive Cocoa 使用 DataBinding。 Here's 一个教程,将解释与 MVVM 的数据绑定和 iOS 中的反应式编程。

【讨论】:

    【解决方案2】:

    我遵循 2 个实践来测试 UIViewController 的各个部分。

    1. MVVM - 使用MVVM 模式,您可以非常轻松地在 ViewModel 类的单元测试中对视图内容进行单元测试。这也使您的 ViewController 逻辑非常轻松,因此您不必编写太多的 UI 测试来涵盖所有这些场景。
    2. KIF - 然后对于 UI 测试,我使用KIF,因为它的测试参与者有助于处理异步和视图加载延迟。使用 KIF,我可以从我的代码中发布通知,我的测试将等待在视图中查看通知处理程序的效果。

    在这两个系统之间,我可以对几乎所有内容进行单元测试,然后非常轻松地编写 UI 测试以涵盖最终部分。

    另外,请注意您的代码:我不会在 viewWillAppear 中添加您的观察者,因为它被多次调用。但是,这可能不是问题,因为您可能不会因为通知合并而收到对处理程序的冗余调用。

    【讨论】:

      猜你喜欢
      • 2013-11-06
      • 2018-02-25
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 2019-07-06
      • 2012-01-08
      • 2011-01-20
      • 2012-12-19
      相关资源
      最近更新 更多