描述
目前,我正在为服务创建一些黄瓜特性。假设我有一个用于配置Tesla汽车的服务:
public class TeslaCar {
Engine engine;
Color color;
// other things here...
}哪里
public class Engine {
boolean isAllWheelDrive;
// for 60, 70, 90D, P100D
KilowattEnum kWh;
// other things here...
}简单的create调用很容易实现,我只是使用Given和Add相结合来配置资源,然后调用create。
所以对于CreateEngine,我会这样做:
Feature: CreateEngine
Scenario Outline: Create Engine
Given an engine was initialized
And engine has <all_wheel>
And kwh is <kwh>
When engine is created
Then engine creation succeeds
Examples:
| kwh | all_wheel |
| 60 | false |
| 60 | true |
| 70 | false |现在我需要为DriveCar编写一个集成测试。我想用Given来确保有一辆我能驾驶的新车。我想制造一辆新车,因为我不知道过去汽车的状况。如果电池水平是10%,这将不是一个好的测试。然后,由于集成测试取决于我所拥有的Tesla的类型,所以我想在我的特性文件中配置它。
所以看起来是:
Feature: Drive Car
Scenario: Test drive Tesla
Given a car is initialized
And engine kwh is P90D
And engine is all wheel drive
And engine is created
# Need to call CreateEngine with above line, but is this clunky?
And car is created
# Need to call CreateCar above because Engine is a parameter to CreateCar
When car is driven
Then max speed is 120mph问题
起初,这似乎相当合理,但有没有更好的方法来做到这一点呢?如果需要为DriveCar配置更多的东西,那么它会很快变得笨重。
问题
可能的解决方案1?
我想到的一件事是使用@tags。这将让我说,‘我需要一个60千瓦的引擎,每当我使用@60kwh’。但这并不是很好的延伸。
可能的解决方案2?
不要配置汽车,而只是使用默认的。
发布于 2016-09-06 20:59:13
黄瓜是为验收测试而设计的,这些测试使用整个系统(因此它们也是集成测试),因为用户将并且可以被涉众以及开发人员理解。
您的CreateEngine场景不是针对整个系统的,而是针对一个只对开发人员有意义的软件组件的,所以我不会用Cucumber,而是用一个单元测试框架来测试它。
另一方面,您的“驾驶汽车”方案是一个适当的验收测试。不过,它有很多软件细节。我会写成这样:
Feature: Drive Car
Scenario: Test drive Tesla
Given there is a car with an all-wheel-drive engine and P90D engine kwh
When the car is driven
Then the max speed is 120 mph(我不确定"P90D engine kwh“是否正确语法,因此请根据需要进行更正。)
要点:
我不确定你是否需要比我展示的更灵活的汽车定义步骤。我通常发现,在大多数情况下只需要几个简单的步骤。一开始试着保持简单,只有当你需要的时候才构建更复杂的步骤。
此外,不要急于为所有数据组合编写Cucumber场景(或场景大纲)。验收测试速度慢,需要维护,因此您希望在向涉众公开所有重要需求的同时尽可能少使用这些测试。当您开始编写组合Cucumber场景时,请考虑是否可以编写一个Cucumber场景作为示例,并测试单元测试中的所有组合。
发布于 2016-09-06 20:44:12
我会压缩步骤,以添加一个组件到汽车,如发动机。使用DataTable添加引擎的所有属性。从这个仅以DataTable作为参数的新步骤定义中,您可以调用现有的引擎设置代码。这样,每次添加新属性时都不需要添加新的步骤定义。只需附加到DataTable的末尾即可。
如果您有一个新的汽车组件,您只需要添加3行到您的功能文件加上一个步骤定义。例如,我在特性中添加了一个变速箱。
如果在特性文件DataTable中,您将表头命名为实例变量,那么如果您将参数设置为List<Engine> engine而不是DataTable,Cucumber将自动将这些值放入类中。
我看到的问题是,如果您有一个具有多个设置的属性。例如,引擎模式可以是城市,邮轮和体育。也许您可以使用逗号分隔字符串,然后再拆分它。
Feature: Drive Car
Scenario: Test drive Tesla
Given A car with following components
And Add engine with specifications
| kwh | allwheel |
| P90D | true |
And Add gearbox with specifications
| noofgears | auto |
| 4 | true |
And assemble car
When car is driven
Then max speed is 120mph如果您想要更多的乐趣,请考虑将其作为场景大纲,并在一个特性中测试多个组合。但是,您将有一个大示例表中的数据行。我实际上会使用该路由,因为我将从一个场景中获得更多信息,它将数据从步骤中推到示例表中。
特性文件中的步骤似乎是对代码的总结,而不是像步骤那样的行为--引擎已经初始化。但是如果每个人都理解它并为你的目的服务,为什么要改变它呢?
发布于 2016-09-07 09:40:41
非常好的问题,戴夫·施魏斯古斯给出了一个很好的答案,我也会补充一下。
当您有一个像您的汽车这样的结构及其所有配置选项,并且您希望编写集成测试来处理不同类型的汽车时,您可以通过命名这些汽车来消除在您的特性中指定每个注意事项的细节的需要。例如,我可能有:
然后,您将编写如下功能:
Given I have a rally car
...
Given I have a taxi等等
现在,重要的是,在你的时候,你不要求助于回到细节,例如
Given I have a taxi
Then I should have 2 wheel drive
And I should have 4 gears这是不好的,因为你混合了两个抽象层次,一个是详细的,另一个是高级的。相反,您应该为出租车编写出租车场景
例如:
Given I have a taxi
When my passengers puke over my interior
Then it should be easy to clean这意味着您的名字需要对您的利益相关者很重要。
您从这种方法中得到的一件事是,当您更改规范时,可以降低更改的成本。例如,如果我们决定出租车应该是4wd,我们不需要改变每个场景,我们只需更改步骤def‘如果我有一辆出租车’。
我将按以下方式实现步骤防御
Given 'I have a taxi' do
create_taxi
end
module TaxiStepHelper
def create_taxi
create_car(
engine:
drive:
...
)
end我认为这种方法是“使用更高层次的抽象”。但是马特·韦恩想出了一种很好的方式来描述它,那就是“如何往下推”。我们正在做的是通过步骤定义将如何配置car的责任推给步骤定义助手。
https://stackoverflow.com/questions/39355957
复制相似问题