使用Gherkin DSL和CppUnitTestFramework进行行为驱动开发

在软件开发过程中,行为驱动开发BDD)是一种强调软件行为和业务需求的测试方法。它通过使用自然语言来定义测试案例,使得非技术人员也能参与到测试用例的编写中。Gherkin DSL(Domain Specific Language)是BDD中常用的一种语言,它允许开发者以易于理解的格式来描述软件的行为。

当一个现有的项目使用Visual Studio的CppUnitTestFramework进行单元测试时,引入使用Gherkin DSL编写的BDD测试意味着需要切换测试框架并添加其他依赖,如果使用Cucumber-CPP的话。为了简化这个过程,可以编写一个Python 2.7脚本来解析Gherkin特征文件,并生成适用于CppUnitTestFramework的桩代码。

本文将展示如何使用附带的脚本和头文件。读者可以自行修改脚本,以生成他们偏好的测试框架和工作语言的输出。

测试用例

文件 "example.feature" 包含了使用Gherkin编写的测试用例。

Feature: Accumulator Background: Given an initial Scenario Outline: Add one other When you add a Then the result is Examples: | value | second | sum | | 1 | 2 | 3 | | 2 | 2 | 4 | Scenario Outline: Add two others When you add a And you add a Then the result is 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 #include 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); \ } namespace Example { TEST_CLASS(Accumulator) { static std::streambuf * oldBuffer; static std::shared_ptr 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; } 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); } TEST_CLASS_INITIALIZE(ClassInitialize) { newBuffer = std::make_shared(); 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; } 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 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) ========== from tests named: AddOneOther123 AddOneOther224 AddTwoOthers1236 AddTwoOthers2349

要点

在实现脚本之前,测试类只包含静态方法,并且存在大量复制粘贴的情况。现在,测试类继承了基类,以便在基类中编写公共步骤,即测试代码更像它测试的内容。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485