BDD“给定”步骤描述及实现
您通常如何描述和实施场景的“给定”步骤?
- 高级状态描述或显式数据定义?
- 填充数据库或存根存储库?
高级状态描述
Given I have 4 products
When I look for best-selling products
Then I see top 3 products with maximum number of sales
PRO
- 不脆弱
- 易于阅读和理解业务目标
CON
- 不清楚我们需要什么数据
显式数据定义
Given I have following products:
| Name | Sales number |
| Beer | 20 |
| Pizza | 5 |
| Socks | 3 |
| Toilet paper | 100 |
When I look for best-selling products
Then I see following products:
| Name | Sales number |
| Toilet paper | 100 |
| Beer | 20 |
| Pizza | 5 |
PRO
- 易于实施(明确需要什么数据)
CON
- 难以阅读和理解业务目标
- 更脆弱
填充数据库
using (var connection = new SqlConnection(connectionString))
{
using (var deleteCommand = new SqlCommand("DELETE FROM Products", connection))
{
connection.Open();
deleteCommand.ExecuteNonQuery();
}
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Products", connection);
DataSet data = new DataSet();
adapter.Fill(data);
foreach (var specFlowRow in table.Rows)
{
DataRow dataRow = data.Tables[0].Rows.Add();
dataRow["Name"] = specFlowRow["Name"];
}
adapter.Update(data);
}
优点
- 规格作为集成测试(我们练习系统端到端)
缺点
- 我们需要在代码之前创建数据库表(数据驱动方法)
- 速度慢
- 脆弱
- 难以实施
存储库存根
// or get stubbed repository from DI framework
productsRepository = new InMemoryProductsRepository();
// or use specflow assist helpers
foreach (var specFlowRow in table.Rows)
productsRepository.Save(new Product(specFlowRow["Name"]));
优点
- 我们可以先编写代码
- 快速
- 不易损坏(易于更改)
- 易于实现
缺点
- 我们没有证据证明功能已实现
这就是我对可能方法的看法:) 您如何定义和实施“给定”步骤? 谢谢!
How do you usually describe and implement 'Given' step for scenario?
- High level state description OR explicit data definitions?
- Fill database or stub repository?
High level state description
Given I have 4 products
When I look for best-selling products
Then I see top 3 products with maximum number of sales
PRO
- Not brittle
- Easy to read and understand business goal
CON
- Do not clear what data we need
Explicit data definition
Given I have following products:
| Name | Sales number |
| Beer | 20 |
| Pizza | 5 |
| Socks | 3 |
| Toilet paper | 100 |
When I look for best-selling products
Then I see following products:
| Name | Sales number |
| Toilet paper | 100 |
| Beer | 20 |
| Pizza | 5 |
PRO
- Easy to implement (clear what data needed)
CON
- Hard to read and understand business goal
- More brittle
Fill database
using (var connection = new SqlConnection(connectionString))
{
using (var deleteCommand = new SqlCommand("DELETE FROM Products", connection))
{
connection.Open();
deleteCommand.ExecuteNonQuery();
}
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Products", connection);
DataSet data = new DataSet();
adapter.Fill(data);
foreach (var specFlowRow in table.Rows)
{
DataRow dataRow = data.Tables[0].Rows.Add();
dataRow["Name"] = specFlowRow["Name"];
}
adapter.Update(data);
}
PRO
- Specs as integration tests (we exercise system end-2-end)
CONS
- We need to create database tables prior to code (data-driven approach)
- Slow
- Brittle
- Hard to implement
Repository stub
// or get stubbed repository from DI framework
productsRepository = new InMemoryProductsRepository();
// or use specflow assist helpers
foreach (var specFlowRow in table.Rows)
productsRepository.Save(new Product(specFlowRow["Name"]));
PRO
- We can do code first
- Fast
- Less brittle (easy to change)
- Easy to implement
CONS
- We do not have proofs that feature is implemented
Thats my vision of possible ways :)
What way do you define and implement 'Given' steps?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我们通过你提到的组合来实现Given。通过使用 DI 和不同的配置(使用此工具可以轻松实现)我们大部分时间在内存中运行单元测试,并强制它们在 CI 服务器上作为针对真实数据库的集成测试。因此您可以获得性能和全面的测试。
为了设置您的数据,我个人最喜欢您的示例“显式数据定义”。指定测试使用哪些数据可确保您可以将测试作为文档来阅读。针对未知的数据存储运行会使测试难以阅读。但在这种情况下构建测试数据时,产品名称并不重要,重要的是数量。
这是通过使用构建器模式来处理的。仅指定为测试导入的数据,并让构建器为所有其他字段生成默认值。
NBuilder 是一个非常好的工具。我们现在正在使用它进行测试,它看起来非常有前途。
您的测试将如下所示:
We implement Given by a combination things you mention. By using DI and different configurations (made easy with this tool) we run our unit tests most of the time in memory and force them once on the CI server as integration tests against a real database. So you get both performance and thorough testing.
For setting up your data, I personally like your example 'Explicit data definition' best. Specifying which data the tests uses makes sure you can read the test as documentation. Running against an unknown data store makes the tests hard to read. But when building your test data in this case, the name of the product is not important, only the amount.
This is handled by using the Builder pattern. Only specify the data that is import for your test and let the Builder generate default values for all other fields.
NBuilder is a really nice tool. We are using it now for our tests and it looks very promising.
Your test would look like:
作为对“我们如何为复杂或相关对象设置给定?”的广泛回答。问题——就像产品和销售——这一切都取决于您指定的行为。没有单一、正确的方法。顺便说一句,您没有包含功能和场景文本来为我们提供有关此 cuke 的行为的一些背景信息,但是,不可否认,这并不难猜测。
您的“不脆弱”的第一个示例展示了一种消除基本行为的好方法,该行为告诉我用户可以看到最畅销产品及其销售数字的简短列表。
如果你想证明显示器在某种程度上关心以排名方式显示事物,你可以更明确地给出,然后明确你是按销量“反向排序”。或者,这个明确的例子可以被认为是让你的意思变得非常清楚。
我通常的经验法则是将设置限制为您想要在当前场景中测试的“对象图”的那些部分。这有助于引起人们对正在测试的系统中最“狭窄”部分的关注。否则,如果你继续为所有场景从头开始构建一切,有时很难看出测试的目的。有时您关心父对象中的细节,有时您想测试各部分的总和。
As a broad answer to the "How do we setup the Given for complex or related objects?" question -- like Products and Sales -- it all depends on what behavior you are specifying. There is no single, right way. BTW, you did not include the Feature and Scenario text to give us some context for what behavior this cuke is for, but, admittedly, it is not hard to guess.
Your "not brittle" first example shows a good way to drive out basic behavior that tells me a user can see a short list of top-selling products and their sales numbers.
If you wanted to prove out that the display somehow cares about showing things in a ranked fashion, your more explicit given and then makes it clear that you are "reverse sorting" by sales volume. Or, this explicit example can be thought of as making it really clear what you mean.
My usual rule of thumb is to limit the setup to just those parts of the "object graph" that you care to test in the current scenario. That helps draw attention to the most "narrow" bits of the system that are under test. Otherwise, if you continue to build everything from scratch for all scenarios, it is sometimes hard to see the purpose of the test. Sometimes you care about the details in the parent object, sometimes you want to test the sum of the parts.