假设我有使用以下方法的类Car:
Car的目的是配置并返回一个IDrivingSession,其中应用程序的其余部分用于驱动汽车。我如何对我的车进行单元测试?
看起来它需要在我调用Go()方法之前完成一系列操作。但我想分别测试每种方法,因为它们都有一些重要的逻辑。我不想要像
那样进行大量的单元测试Test1: LoadGasoline, Assert
Test2: LoadGasoline, InsertKey, Assert
Test3: LoadGasoline, InsertKey, StartEngine, Assert
Test4: LoadGasoline, InsertKey, StartEngine, Go, Assert
是不是有更好的方法来对顺序逻辑进行单元测试,或者这是我的汽车设计的问题?
---编辑---- 感谢所有的答案。正如许多人注意到的那样,我也应该对无效场景进行测试,我也有测试,但这个问题主要集中在如何测试有效序列。
答案 0 :(得分:3)
我认为每种方法都应该单独测试。
恕我直言,您应该为每个案例准备环境,因此如果您更改LoadGasoline方法,只有LoadGasoline测试会中断,并且您不需要因为单个错误而看到所有测试中断。
我不知道你的汽车的状态是怎样的,但是,在InsertKey之前,你应该准备一个类似car.SetTotalGasoline(20)的方法;或者在此方法中设置的任何变量,但不依赖于LoadGasoline方法的复杂逻辑。
稍后您将需要测试(在这种情况下,不是单元测试)来测试所有序列。
答案 1 :(得分:2)
某些单元测试框架允许您指定在实际测试开始之前运行的设置代码。
这允许您在运行测试之前将目标对象置于正确的状态。这样,您的测试可以根据您正在测试的特定代码而不是在运行测试之前所需的代码通过或失败。
结果,您的测试序列将会结束:
Test1:
LoadGasoline, Assert
Test2 Setup:
LoadGasoline
Test2:
InsertKey, Assert
Test3 Setup:
LoadGasoline, InsertKey
Test3:
StartEngine, Assert
Test4 Setup:
LoadGasoline, InsertKey, StartEngine
Test4:
Go, Assert
实际上,由于测试都按顺序进行,因此如果之前的测试通过,则测试设置无法失败。
话虽如此,你还应该测试那些预计不起作用的失败案例,但这是一个不同的问题。
答案 2 :(得分:1)
为什么不想要所有这些测试?
如果您在Go
之前或之后调用它,那么 InsertKey
会有非常不同的行为,对吧?因此,在我看来,你应该测试这两种行为。
答案 3 :(得分:1)
这是一种公平的不情愿,但有时这就是你需要做的事情。如果你不能伪造被测系统,所以它认为它处于以后的状态,那么你需要通过相同的步骤才能使它进入该状态。如果不了解你的测试结果,就不清楚如何伪造不同的状态。
一种可以容忍的方法是使用一个提取方法重构一个状态的测试,以便可以使用相同的代码来准备下一个测试。
答案 4 :(得分:1)
我可能会
Test 1: LoadGasoline, Assert, InsertKey Assert, StartEngine Assert, Go Assert
Test 2: LoadGasoline, Go, Assert
Test 3: Go, Assert
Test 4: StartEngine, Go, Assert
根据实际的对象,我不会尝试做所有的排列,但是我会有一个单一的测试能够达到成功的轨道,然后我会测试那些碰到我的边缘情况。
经过一番思考后,我可能会进行以下测试:
答案 5 :(得分:0)
从技术上讲,您至少应该使用以下测试:
testLoadGasoline
testInsertKeyGasolineNotLoaded
testStartEngineKeyNotInserted
testGoEngineNotStarted
testGo
如果您可以直接查看可以添加的中间步骤
testInsertKeyGasolineLoaded
testStartEngineKeyInserted
请注意,如果您可以直接设置状态(取决于语言和设计),那么
testInsertKeyGasolineLoaded
实际上可能不会调用LoadGasoline
。