一小块黄瓜





5.00/5 (1投票)
使用 Python 2.7 脚本从 Gherkin DSL 特征文件创建 Visual C++ 测试。
更新
本文的代码已被泛化到其他语言的其他测试框架,并已打包供从 PyPI 下载。
引言
采用一个使用 Visual Studio CppUnitTestFramework 的现有项目,并引入用 Gherkin DSL 编写的 BDD 测试,这将意味着如果使用 Cucumber-CPP,则需要切换测试框架并添加其他依赖项。 为了保持简单,编写了一个 Python 2.7 脚本来解析(英语)Gherkin 特征文件,并为 CppUnitTestFramework
生成存根代码。
本文展示了一个使用附加脚本和头文件的示例。 读者可以自行决定修改脚本,以便为其工作语言中喜欢的测试框架生成输出。
测试
文件 "example.feature" 包含用 Gherkin 编写的测试。
Feature: Accumulator
Background:
Given an initial <value>
Scenario Outline: Add one other
When you add a <second>
Then the result is <sum>
Examples:
| value | second | sum |
| 1 | 2 | 3 |
| 2 | 2 | 4 |
Scenario Outline: Add two others
When you add a <second>
And you add a <third>
Then the result is <sum>
Examples:
| value | second | third | sum |
| 1 | 2 | 3 | 6 |
| 2 | 3 | 4 | 9 |
存根代码
运行脚本 "features.py" 会生成 C++ 代码
#include "stdafx.h"
#include "CppUnitTest.h"
#include "TestUtils/LogStream.h"
#include <iostream>
#include <memory>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
#define AddOneOtherInst(_VALUE, _SECOND, _SUM) \
TEST_METHOD(AddOneOther ## _VALUE ## _SECOND ## _SUM) \
{ \
AddOneOther(#_VALUE, #_SECOND, #_SUM); \
}
#define AddTwoOthersInst(_VALUE, _SECOND, _THIRD, _SUM) \
TEST_METHOD(AddTwoOthers ## _VALUE ## _SECOND ## _THIRD ## _SUM) \
{ \
AddTwoOthers(#_VALUE, #_SECOND, #_THIRD, #_SUM); \
}
其中两个场景大纲被表示为宏。 请注意,参数作为 string
传递,这可能不是期望的行为。 测试方法名称是通过连接形成的,在这种情况下 "## _SUM
" 是多余的,在其他情况下,例如句点之类的字符可能会被传递,从而导致编译错误。 此外,由于没有连接分隔符,测试名称可能不一定是唯一的,即最好编辑生成的名称。
namespace Example
{
TEST_CLASS(Accumulator)
{
static std::streambuf* oldBuffer;
static std::shared_ptr<std::streambuf> newBuffer;
命名空间只是文件名减去其扩展名,类名是特征名。
void GivenAnInitial(std::string value)
{
std::clog << " Given an initial " << value << std::endl;
}
void WhenYouAddA(std::string second)
{
std::clog << " When you add a " << second << std::endl;
}
void ThenTheResultIs(std::string sum)
{
std::clog << " Then the result is " << sum << std::endl;
}
场景的步骤被实现为单独的方法。 要实现实际的测试,必须声明一个类变量来表示总和,由第一个方法初始化,由第二个方法递增,并在第三个方法中进行测试。 显然,如果将参数更改为 int
类型,则会更好。
static void AddOneOther(std::string value, std::string second, std::string sum)
{
std::clog << " Feature: Accumulator" << std::endl;
std::clog << " Scenario: Add one other" << std::endl;
Accumulator instance;
instance.GivenAnInitial(value);
instance.WhenYouAddA(second);
instance.ThenTheResultIs(sum);
}
static void AddTwoOthers(std::string value, std::string second, std::string third, std::string sum)
{
std::clog << " Feature: Accumulator" << std::endl;
std::clog << " Scenario: Add two others" << std::endl;
Accumulator instance;
instance.GivenAnInitial(value);
instance.WhenYouAddA(second);
instance.WhenYouAddA(third);
instance.ThenTheResultIs(sum);
}
场景大纲被实现为 static
函数,由它们对应的宏调用。
TEST_CLASS_INITIALIZE(ClassInitialize)
{
newBuffer = std::make_shared<TestUtils::LogStream>();
oldBuffer = std::clog.rdbuf(newBuffer.get());
std::clog << "Entering Example" << std::endl;
}
TEST_CLASS_CLEANUP(ClassCleanup)
{
std::clog << "Exiting Example" << std::endl;
std::clog.rdbuf(oldBuffer);
newBuffer = nullptr;
}
Visual Studio Logger
类被包装在一个 stream
中,该 stream
暂时取代了 std::clog
使用的那个。
public:
AddOneOtherInst(1, 2, 3);
AddOneOtherInst(2, 2, 4);
AddTwoOthersInst(1, 2, 3, 6);
AddTwoOthersInst(2, 3, 4, 9);
};
std::streambuf* Accumulator::oldBuffer = nullptr;
std::shared_ptr<std::streambuf> Accumulator::newBuffer = nullptr;
}
这四个测试被实现为宏的实例。
输出
构建并运行会生成输出
------ Run test started ------
Entering Example
Feature: Accumulator
Scenario: Add two others
Given an initial 2
When you add a 3
When you add a 4
Then the result is 9
Feature: Accumulator
Scenario: Add two others
Given an initial 1
When you add a 2
When you add a 3
Then the result is 6
Feature: Accumulator
Scenario: Add one other
Given an initial 2
When you add a 2
Then the result is 4
Feature: Accumulator
Scenario: Add one other
Given an initial 1
When you add a 2
Then the result is 3
Exiting Example
========== Run test finished: 4 run (0:00:00.6816458) ==========
来自名为的测试
AddOneOther123
AddOneOther224
AddTwoOthers1236
AddTwoOthers2349
关注点
在实现脚本之前,我的测试类只有 static
方法,并且表现出使用复制和粘贴。 现在,我的测试类继承了,因此公共步骤是在基类中编写的,即,我的测试代码看起来更像它测试的内容。
历史
- 2016/03/09:首次发布
- 2019/10/09:在 PyPI 上添加了 Cornichon 的链接