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
  • AtabBarHeight被退回
  • 一个观察者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

该测试现在通过了,但这是测试它的正确/最佳方法吗?

4

2 回答 2

4

你刚刚感受到了 iOS ViewControllers 的一大缺点:它们在可测试性方面很烂

  • ViewControllers 混合管理视图和模型的逻辑
    • 这导致了大量的 ViewController
    • 这违反了单一职责规则
      • 这使得代码不可重用

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

文章 - 来源

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

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

于 2015-03-04T08:44:36.897 回答
2

我遵循 2 种做法来测试 UIViewController 的各个部分。

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

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

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

于 2015-03-02T01:59:42.137 回答