动画如何进行单元测试?

Mar*_*ein 10 tdd cocoa animation design-patterns ios

现代用户界面,尤其是MacOS和iOS,具有大量"随意"动画 - 通过系统主要协调的简短动画序列显示的视图.

[[myNewView animator] setFrame: rect]
Run Code Online (Sandbox Code Playgroud)

偶尔,我们可能会有更精细的动画,有动画组和完成块.

现在,我可以想象这样的错误报告:

嘿 - myNewView出现的好动画在新版本中没有发生!

所以,我们希望单元测试做一些简单的事情:

  • 确认动画发生了
  • 检查动画的持续时间
  • 检查动画的帧速率

但是,当然所有这些测试都必须易于编写,并且不能使代码变得更糟; 我们不想用大量的测试驱动的复杂性来破坏隐式动画的简单性!

那么,什么是TDD友好的方法来实现休闲动画的测试?


单元测试的理由

让我们举一个具体的例子来说明我们为什么要进行单元测试.假设我们有一个包含一堆WidgetViews的视图.当用户通过双击创建一个新的Widget时,它应该最初看起来很小且透明,在动画期间扩展到完整大小.

现在,我们确实不需要单元测试系统行为.但是有些事情可能会出错,因为我们犯了错误:

  1. 动画在错误的线程上调用,并且不会被绘制.但是在动画过程中,我们调用setNeedsDisplay,因此最终会得到小部件.

  2. 我们正在从废弃的WidgetControllers池中回收废弃的小部件.新的WidgetViews最初是透明的,但回收池中的某些视图仍然是不透明的.所以褪色不会发生.

  3. 在动画结束之前,在新窗口小部件上会启动一些额外的动画.结果,小部件开始出现,然后在它稳定下来之前开始猛拉和闪烁.

  4. 您对窗口小部件的drawRect:方法进行了更改,新的drawRect很慢.旧动画很好,但现在很糟糕.

所有这些都将在您的支持日志中显示为"创建窗口小部件动画不再起作用".我的经验是,一旦你习惯了动画,开发人员很难立即注意到一个不相关的变化打破了动画.这是单元测试的秘诀,对吧?

Fru*_*eek 4

动画在错误的线程上调用,并且不会被绘制。但在动画过程中,我们调用 setNeedsDisplay,因此最终会绘制小部件。

不要直接对此进行单元测试。当动画位于不正确的线程上时,请使用断言和/或引发异常。单元测试断言将适当地引发异常。苹果公司在他们的框架上积极地做到了这一点。它可以防止你搬起石头砸自己的脚。当您使用有效参数之外的对象时,您会立即知道。

我们正在从废弃的 WidgetController 池中回收废弃的小部件。新的 WidgetView 最初是透明的,但回收池中的某些视图仍然是不透明的。所以不会发生褪色。

这就是为什么您会看到类似dequeueReusableCellWithIdentifierUITableView 中的方法。您需要一个公共方法来获取重用的 WidgetView,这是测试适当重置 alpha 等属性的绝佳机会。

在动画完成之前,新小部件上会启动一些附加动画。结果,小部件开始出现,然后开始抖动并短暂闪烁,然后才稳定下来。

与第 1 点相同。使用断言将规则强加于代码。可以触发断言的单元测试。

您对小部件的drawRect: 方法进行了更改,并且新的drawRect 速度很慢。以前的动画还不错,现在就乱了。

单元测试可以只是对方法进行计时。我经常通过计算来确保它们保持在合理的时间限制内。

-(void)testAnimationTime
{
    NSDate * start = [NSDate date];
    NSView * view = [[NSView alloc]init];
    for (int i = 0; i < 10; i++)
    {
        [view display];
    }

    NSTimeInterval timeSpent = [start timeIntervalSinceNow] * -1.0;

    if (timeSpent > 1.5)
    {
        STFail(@"View took %f seconds to calculate 10 times", timeSpent);
    }
}
Run Code Online (Sandbox Code Playgroud)