0

这是我的具体情况。

我有一个将类QueryQueue包装QueryTask在 ArcGIS API for Flex 中的类。这使我能够轻松地将多个查询任务排队等待执行。调用QueryQueue.execute()遍历我队列中的所有任务并调用它们的执行方法。

当所有结果都被接收和处理后,QueryQueue将调度完成的事件。我的班级的界面非常简单。

public interface IQueryQueue
{
    function get inProgress():Boolean;
    function get count():int;

    function get completed():ISignal;
    function get canceled():ISignal;

    function add(query:Query, url:String, token:Object = null):void; 
    function cancel():void;
    function execute():void;
}

为了使该QueryQueue.execute方法被认为是成功的,必须发生几件事。

  1. task.execute必须在每个查询任务上调用一次且仅一次
  2. inProgress = true结果待定
  3. inProgress = false处理结果时
  4. completed在处理结果后调度
  5. canceled从未被调用
  6. 队列内完成的处理正确处理和打包查询结果

我正在努力将这些测试分解为可读、合乎逻辑和可维护的测试。

从逻辑上讲,我正在测试一种状态,即成功执行状态。这表明将断言 #1 到 #6 上面的一个单元测试是正确的。

[Test] public mustReturnQueryQueueEventArgsWithResultsAndNoErrorsWhenAllQueriesAreSuccessful:void

但是,测试的名称并不提供信息,因为它没有描述所有必须为真才能被视为通过测试的事情。

在线阅读(包括在这里和在programmers.stackexchange.com)有一个相当大的阵营断言单元测试应该只有一个断言(作为指导)。结果,当测试失败时,您确切地知道失败的原因(即 inProgress 未设置为 true、已完成显示多次等)。您最终可能会遇到更多(但理论上更简单、更清晰)的测试,如下所示:

[Test] public mustInvokeExecuteForEachQueryTaskWhenQueueIsNotEmpty():void
[Test] public mustBeInProgressWhenResultsArePending():void
[Test] public mustNotInProgressWhenResultsAreProcessedAndSent:void
[Test] public mustDispatchTheCompletedEventWhenAllResultsProcessed():void
[Test] public mustNeverDispatchTheCanceledEventWhenNotCanceled():void
[Test] public mustReturnQueryQueueEventArgsWithResultsAndNoErrorsWhenAllQueriesAreSuccessful:void
// ... and so on

这可能会导致测试中出现大量重复代码,但可以通过适当setupteardown方法将其最小化。

虽然这个问题与其他问题相似,但我正在寻找这个特定场景的答案,因为我认为它很好地代表了一个复杂的单元测试场景,展示了需要验证的多个状态和行为。不幸的是,许多其他问题没有示例,或者示例没有展示复杂的状态和行为。

4

3 回答 3

2

在我看来,可能会有很多,这里有几件事:

  1. 如果您必须为一种方法测试这么多东西,那么这可能意味着您的代码可能在一种方法中做了太多事情(单一责任原则)
  2. 如果您不同意上述内容,那么我接下来要说的是,您所描述的更多的是集成/验收测试。这允许多个断言,并且您在那里没有问题。但是,请记住,如果您正在进行自动化测试(安全测试与不安全测试),这可能需要归入单独的测试部分
  3. 和/或者,是的,首选方法是分别测试每个部分,因为这就是单元测试。我可以建议的最接近的事情,这是关于你对编写代码的容忍度只是为了进行完美的测试......是检查一个对象与一个对象(所以你会做一个基本上测试这一切的断言)。然而,反对这一点的论点是,是的,它通过了每个测试测试的一个断言,但你仍然失去了表达能力。

最终,您的目标应该是通过关注SOLID 原则来努力实现理想(每个单元测试一个断言) ,但最终您确实需要完成工作,否则编写软件没有真正意义(至少我的观点是: ))。

于 2012-03-16T14:01:32.627 回答
1

让我们关注您首先确定的测试。除了最后一个 ( mustReturnQueryQueueEventArgs...) 之外的所有内容都很好,我可以立即知道那里正在测试什么(这是非常好的迹象,表明它们是描述性的并且很可能很简单)。

唯一的问题是你的最后一次测试。请注意,在测试名称中大量使用单词“and”“with”“or”通常会响起问题。目前还不是很清楚它应该做什么。首先想到返回正确的结果,但有人可能会认为这是一个模糊的术语?这是正确的,它是模糊的。但是你经常会发现这确实是一个非常普遍的需求,在方法/操作契约中有详细的描述。

在您的特定情况下,我会简化最后一次测试以验证是否返回了正确的结果,仅此而已。您已经测试了导致结果构建的状态、事件和内容,因此无需再次这样做。

现在,您提供的链接中的建议实际上是非常好的建议,通常,我建议坚持使用它们(一个测试的单一断言)。问题是,单一断言真正代表什么?测试结束时的 1 行代码?那么让我们考虑这个简单的例子:

// a method which updates two fields of our custom entity, MyEntity
public void Update(MyEntity entity)
{
    entity.Name = "some name";
    entity.Value = "some value";
}

此方法合同将执行这两个操作。通过成功,我们理解实体被正确更新。如果其中一个由于某种原因失败,则作为一个单元的方法被认为是失败的。你可以看到这是怎么回事;您要么有两个断言,要么纯粹出于测试目的编写自定义比较器。

不要被单一断言所欺骗;这与代码行数或断言数量无关(但是,在大多数测试中,您将编写它确实会映射 1:1),而是关于断言单个单元(在上面的示例中,更新被认为是一个单元)。并且单位实际上可能是多个没有彼此就没有任何意义的东西。

这正是您引用的问题之一(Roy Osherove):

我的指导方针通常是每次测试都测试一个逻辑概念。你可以在同一个对象上有多个断言。它们通常是被测试的相同概念。

这都是关于概念/责任的;不是断言的数量。

于 2012-03-16T14:26:39.593 回答
0

我不熟悉 flex,但我认为我在单元测试方面有很好的经验,所以你必须知道单元测试是一种哲学,所以对于第一个答案,是的,你可以进行多重断言,但如果你测试相同的行为,单元测试的重点始终是非常易于维护和简单的代码,否则单元测试将需要单元测试来测试它!所以我给你的建议是,如果你是单元测试新手,不要使用多个断言,但如果你有良好的单元测试经验,你就会知道什么时候需要使用它们

于 2012-03-17T20:55:20.780 回答