65.9K
CodeProject 正在变化。 阅读更多。
Home

一小块黄瓜

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2016年3月9日

CPOL

2分钟阅读

viewsIcon

20814

downloadIcon

154

使用 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 的链接
© . All rights reserved.