简体   繁体   English

在测试复杂行为时,可以在一个单元测试中包含多个断言吗?

[英]Is it OK to have multiple assertions in a unit test when testing complex behavior?

Here is my specific scenario. 这是我的具体情况。

I have a class QueryQueue that wraps the QueryTask class within the ArcGIS API for Flex. 我有一个QueryQueue类, QueryQueueQueryTask类包装在ArcGIS API for Flex中。 This enables me to easily queue up multiple query tasks for execution. 这使我可以轻松地将多个查询任务排队等待执行。 Calling QueryQueue.execute() iterate through all the tasks in my queue and call their execute method. 调用QueryQueue.execute()遍历队列中的所有任务,并调用它们的execute方法。

When all the results have been received and processed QueryQueue will dispatch the completed event. 接收并处理所有结果后, QueryQueue将调度完成的事件。 The interface to my class is very simple. 我班的接口很简单。

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;
}

For the QueryQueue.execute method to be considered successful several things must occur. 为了使QueryQueue.execute方法成功,必须发生一些事情。

  1. task.execute must be called on each query task once and only once 必须对每个查询任务一次调用task.execute
  2. inProgress = true while the results are pending 结果等待处理时, inProgress = true
  3. inProgress = false when the results have been processed 处理结果后, inProgress = false
  4. completed is dispatched when the results have been processed completed结果后调度completed
  5. canceled is never called canceled从未被调用
  6. The processing done within the queue correctly processes and packages the query results 队列中完成的处理正确处理并打包了查询结果

What I am struggling with is breaking these tests into readable, logical, and maintainable tests. 我正在努力将这些测试分解为可读,逻辑和可维护的测试。

Logically I am testing one state , that is the successful execution state. 从逻辑上讲,我正在测试一种状态 ,即成功执行状态。 This would suggest one unit test that would assert #1 through #6 above are true. 这表明一个单元测试可以断定上面的#1至#6是正确的。

[Test] public mustReturnQueryQueueEventArgsWithResultsAndNoErrorsWhenAllQueriesAreSuccessful:void

However, the name of the test is not informative as it does not describe all the things that must be true in order to be considered a passing test. 但是,测试的名称并不能说明所有问题,因为它没有描述要通过测试就必须真实的所有事物。

Reading up online (including here and at programmers.stackexchange.com ) there is a sizable camp that asserts that unit tests should only have one assertion (as a guideline). 在线阅读(包括在此处以及在developers.stackexchange.com上 ),有一个相当大的阵营,它断言单元测试应该只有一个断言(作为准则)。 As a result when a test fails you know exactly what failed (ie inProgress not set to true, completed displayed multiple times, etc.) You wind up with potentially a lot more (but in theory simpler and clearer) tests like so: 结果,当测试失败时,您会确切地知道失败的原因(即,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

This could wind up with a lot of repeated code in the tests, but that could be minimized with appropriate setup and teardown methods. 在测试中可能会产生大量重复的代码,但是可以通过适当的setupteardown方法将其最小化。

While this question is similar to other questions I am looking for an answer for this specific scenario as I think it is a good representation of a complex unit testing scenario exhibiting multiple states and behaviors that need to be verified. 尽管此问题与其他问题类似,但我正在寻找针对此特定方案的答案,因为我认为它很好地表示了一个复杂的单元测试方案,该方案具有多个状态和行为,需要进行验证。 Many of the other questions have, unfortunately, no examples or the examples do not demonstrate complex state and behavior. 不幸的是,许多其他问题都没有示例,或者这些示例没有展示复杂的状态和行为。

In my opinion, and there will probably be many, there are a couple of things here: 我认为可能会有很多,这里有几件事:

  1. If you must test so many things for one method, then it could mean your code might be doing too much in one single method ( Single Responsibility Principle) 如果您必须为一种方法测试很多东西,那么这可能意味着您的代码可能在一种方法中做得太多( 单一职责原则)
  2. If you disagree with the above, then the next thing I would say is that what you are describing is more of an integration/acceptance test. 如果您不同意以上内容,那么我接下来要说的是,您所描述的更多是集成/验收测试。 Which allows for multiple asserts, and you have no problems there. 它允许多个断言,并且那里没有问题。 But, keep in mind that this might need to be relegated to a separate section of tests if you are doing automated tests (safe versus unsafe tests) 但是请记住,如果您要进行自动测试(安全测试与不安全测试),则可能需要将其放到单独的测试部分中。
  3. And/Or, yes, the preferred method is to test each piece separately as that is what a unit test is. 和/或,是的,首选方法是分别测试每个部分,因为这就是单元测试。 The closest thing I can suggest, and this is about your tolerance for writing code just to have perfect tests...Is to check an object against an object (so you would do one assert that essentially tests this all in one). 我可以建议的最接近的东西,这是关于您对代码的容忍度,以使其具有完美的测试...是针对一个对象检查一个对象(因此,您需要做一个断言,实质上就是对所有对象进行一次测试)。 However, the argument against this is that, yes it passes the one assert per test test, but you still lose expressiveness. 但是,反对这一观点的论点是,是的,它通过了每个测试的一个断言,但是您仍然失去了表现力。

Ultimately, your goal should be to strive towards the ideal (one assert per unit test) by focusing on the SOLID principles , but ultimately you do need to get things done or else there is no real point in writing software (my opinion at least :)). 最终,您的目标应该是通过专注于SOLID原则来追求理想的状态(每个单元测试一个断言),但是最终您确实需要完成工作,否则编写软件没有任何实际意义(至少我认为: ))。

Let's focus on the tests you have identified first. 让我们专注于您首先确定的测试。 All except the last one ( mustReturnQueryQueueEventArgs... ) are good ones and I could immediatelly tell what's being tested there (and that's very good sign, indicating they're descriptive and most likely simple). 除了最后一个( mustReturnQueryQueueEventArgs... )之外,其他所有都是好东西,我可以立即告诉那里正在测试什么(这是一个很好的信号,表明它们具有描述性,并且很可能很简单)。

The only problem is your last test. 唯一的问题是您的最后一次测试。 Note that extensive use of words "and" , "with" , "or" in test name usually rings problems bell. 请注意,测试名称中单词“ and”“ with”“ or”的广泛使用通常会引起问题。 It's not very clear what it's supposed to do. 目前尚不清楚应该做什么。 Return correct results comes to mind first, but one might argue it's vague term? 返回正确的结果首先想到的是,但有人可能会说这是一个模糊的术语? This holds true, it is vague. 这是正确的,这是模糊的。 However you'll often find out that this is indeed pretty common requirement, described in details by method/operation contract. 但是,您经常会发现这确实是相当普遍的要求,并按方法/操作合同进行了详细说明。

In your particular case, I'd simplify last test to verify whether correct results are returned and that would be all. 在您的特定情况下,我将简化上一次测试,以验证是否返回了正确的结果,仅此而已。 You tested states, events and stuff that lead to results building already, so there is no need to that again. 您已经测试了导致结果建立的状态,事件和事物,因此无需再次进行测试。

Now, advices in links you provided are quite good ones actually, and generally , I suggest sticking to them (single assertion for one test). 现在,您提供的链接中的建议实际上是非常好的建议, 通常 ,我建议您坚持使用它们(对一个测试使用单个断言)。 The question is, what single assertion really stands for? 问题是, 单个断言真正代表什么? 1 line of code at the end of test? 测试结束时有1行代码? Let's consider this simple example then: 然后让我们考虑这个简单的示例:

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

This method contract is to perform those 2 operations. 该方法合同将执行这两个操作。 By success, we understand entity to be correctly updated. 通过成功,我们了解要正确更新的实体。 If one of them for some reasons fails, method as a unit is considered to fail. 如果其中一个由于某种原因失败,则将方法作为一个单元视为失败。 You can see where this is going; 您可以看到前进的方向; you'll either have two assertions or write custom comparer purely for testing purposes. 您将拥有两个断言,或者仅出于测试目的编写自定义比较器。

Don't be tricked by single assertion ; 不要被单一的主张所欺骗; it's not about lines of code or number of asserts (however, in majority of tests you'll write this will indeed map 1:1), but about asserting single unit (in the example above, update is considered to be an unit). 这与代码行数或断言数量无关(但是,在大多数测试中,您编写的确会映射为1:1),而是断言单个单元 (在上面的示例中, update被视为一个单元)。 And unit might be in reality multiple things that don't make any sense at all without eachother. 实际上,单位可能是多种事物,彼此之间毫无任何意义。

And this is exactly what one of questions you linked quotes (by Roy Osherove): 这正是您链接引号的问题之一(Roy Osherove):

My guideline is usually that you test one logical CONCEPT per test. 我的指导原则通常是每次测试都测试一个逻辑概念。 you can have multiple asserts on the same object. 您可以在同一对象上具有多个断言。 they will usually be the same concept being tested. 它们通常是要测试的相同概念。

It's all about concept/responsibility; 都是关于概念/责任; not the number of asserts. 不是断言的数量。

I am not familiar with flex, but I think I have good experience in unit testing, so you have to know that unit test is a philosophy, so for the first answer, yes you can make a multiple assert but if you test the same behavior, the main point always in unit testing is to be very maintainable and simple code, otherwise the unit test will need unit test to test it! 我不熟悉flex,但是我想我在单元测试方面有很好的经验,因此您必须知道单元测试是一种哲学,因此对于第一个答案,可以进行多重声明,但是如果您测试相同的行为,单元测试中的重点始终是非常易于维护和简单的代码,否则单元测试将需要单元测试来进行测试! So my advice to you is, if you are new in unit testing, don't use multiple assert, but if you have good experience with unit testing, you will know when you will need to use them 因此,我的建议是,如果您是单元测试的新手,请不要使用多个断言,但是如果您在单元测试方面有丰富的经验,那么您会知道何时需要使用它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM