是否可以以编程方式向场景添加行?

时间:2014-07-07 12:22:17

标签: specflow

我想在每个SpecFlow测试的开头添加相同的行。

此行指定了一些随时间变化的场景列表,因此不可能为每个测试维护此列表。

例如:

Given I have set my site theme to <MyTheme>
    |Theme Names|
    |Theme 1    |
    |Theme 2    |
    |Theme 3    |
    |Theme 4    |
    |Theme 5    |

我希望每个主题都重复此测试。主题列表不是一成不变的,应该保存在一个地方。

到目前为止,我已成功设法创建Generator Plugin,我计划在生成测试类之前立即使用此插件更改SpecFlow功能。但是,我无法在此上下文中看到编辑方案的方法。

是否可以从IUnitTestGeneratorProvider的实现中获取和设置方案文本?

我没有开始使用这种方法,所以如果有人可以提出更好的方法来做到这一点,那么我也会接受。

如果我的某些术语出错了,我会道歉 - 我只是刚开始使用SpecFlow。

修改

我添加此部分是为了澄清我实际上在追求的内容。

假设我有一个包含800个测试的测试套件。我们有业务要求在每个可用主题上运行800个测试中的每一个。可用主题列表可以随时更改,将此列表保留在多个位置是不可行的。

因此,例如,如果我进行了以下两项测试:

示例A:

    Given I set context to < site >
    Given I go to base url
    When I type <username> in username field
    When I type <password> in password field
    When I click login button
    Examples:
        | site         | username  | password   |
        | MySuperSite  | chris     | mypassword |
        | MySuperSite2 | chris2    | mypassword |



    Given I am logged in
    Given I go to base url
    When I click logout button
    Then I am logged out

我可以简单地手动将这些测试更改为:

例B:

    Given I am using the < theme > theme
    Given I set context to < site >
    Given I go to base url
    When I type <username> in username field
    When I type <password> in password field
    When I click login button
    Examples:
        | site         | username  | password   | theme  |
        | MySuperSite  | chris     | mypassword | theme1 |
        | MySuperSite2 | chris2    | mypassword | theme1 |
        | MySuperSite  | chris     | mypassword | theme2 |
        | MySuperSite2 | chris2    | mypassword | theme2 |
        | MySuperSite  | chris     | mypassword | theme3 |
        | MySuperSite2 | chris2    | mypassword | theme3 |



    Given I am using the < theme > theme
    Given I am logged in
    Given I go to base url
    When I click logout button
    Then I am logged out
    Examples:
        | theme  |
        | theme1 |
        | theme2 |
        | theme3 |

这有一些问题:

  • 测试会因重复数据而变得臃肿
  • 如果我们不再选择支持theme2,那么有人必须完成每个测试并将其从示例表中删除(在上面的示例中不是太糟糕,但是我们的成像是> 800次测试)< / LI>
  • 已有示例表的测试将其表格大小乘以我们支持的主题数量(&gt; 40)
  • 由于某人无意中忘记为测试添加特定主题而导致手动错误的风险很大

目的: 我希望能够让我们的测试人员以Example A的方式编写测试,但如果他们以Example B的风格编写,那么测试本身就会编译成测试结果。

我已经为specflow创建了一个生成器插件,其中包含拦截测试创建的视图,然后以编程方式添加行Given I am using the < theme > theme,然后根据需要更新或添加任何示例数据。但是,我似乎无法从这里做到这一点。

任何人都可以告诉我这是否可行,如果是的话,我应该怎么做呢?

2 个答案:

答案 0 :(得分:5)

好的,我想出来了。它花了几个步骤:

在浏览GitHub上的源代码后,我发现UnitTestFeatureGenerator似乎是负责将specflow文件转换为单元测试的类。

然后我创建了一个继承自UnitTestFeatureGenerator的新类,并从基类中隐藏了GenerateUnitTestFixture方法。

在我的GenerateUnitTestFixture类的主体中,然后在移交base.GenerateUnitTestFixture之前添加场景中所需的额外步骤以生成单元测试。这是它的要点:

public class MultiThemeUnitTestFeatureGenerator : UnitTestFeatureGenerator, IFeatureGenerator
{
    public MultiThemeUnitTestFeatureGenerator(IUnitTestGeneratorProvider testGeneratorProvider, CodeDomHelper codeDomHelper, GeneratorConfiguration generatorConfiguration, IDecoratorRegistry decoratorRegistry)
        : base(testGeneratorProvider, codeDomHelper, generatorConfiguration, decoratorRegistry)
    {}

    public new CodeNamespace GenerateUnitTestFixture(Feature feature, string testClassName, string targetNamespace)
    {
        foreach (var scenario in feature.Scenarios)
        {
            scenario.Steps.Insert(0, new Given {Text = "Given I have <Theme> set as my current theme"});

            //add any other steps you need....
        }

        return base.GenerateUnitTestFixture(feature, testClassName, targetNamespace);
    }
}

一旦我完成了所有这些设置,我需要一种方法来告诉specflow使用我的新类而不是当前注册的UnitTestFeatureGenerator。这是一个复杂的工作,因为文档几乎只是说“即将推出”。谢天谢地,我found an excellent blog post which outlines all the pitfalls.

我的IGeneratorPlugin实现如下:

public class MultiThemeGeneratorPlugin : IGeneratorPlugin
{
    public void RegisterDependencies(ObjectContainer container)
    {}

    public void RegisterCustomizations(ObjectContainer container, SpecFlowProjectConfiguration generatorConfiguration)
    {
        container.RegisterTypeAs<MultiThemeFeatureGeneratorProvider, IFeatureGeneratorProvider>("default");
    }

    public void RegisterConfigurationDefaults(SpecFlowProjectConfiguration specFlowConfiguration)
    {}
}

注意我注册IFeatureGeneratorProvider而不是IFeatureGenerator。我必须创建一个IFeatureGeneratorProvider的实现,它返回我感兴趣的IFeatureGenerator实现的实例:

public class MultiThemeFeatureGeneratorProvider : IFeatureGeneratorProvider
{
    private readonly ObjectContainer _container;

    public MultiThemeFeatureGeneratorProvider(ObjectContainer container)
    {
        _container = container;
    }

    public int Priority
    {
        get { return int.MaxValue; }
    }

    public bool CanGenerate(Feature feature)
    {
        return true;
    }

    public IFeatureGenerator CreateGenerator(Feature feature)
    {
        return _container.Resolve<MultiThemeUnitTestFeatureGenerator>();
    }
}

答案 1 :(得分:1)

我无法想到一种简单的方法,但我的方法可能是从测试中排除主题,但让构建服务器在已经设置主题的环境中运行测试(通过环境变量或通过一些配置文件或类似文件)然后运行整个套件n次,每个主题一次。

所以我希望场景看起来像这样

Given I am using the currently configured theme
Given I am logged in
Given I go to base url
When I click logout button
Then I am logged out

其中第一步从配置文件或环境变量中读取主题,并设置测试以使用该主题。

一旦构建服务器运行了这些测试,它就会将config / environment变量更改为下一个主题并再次运行该套件。

这使您能够在单个位置(在构建服务器上或在BS用于发现主题的文件中)指定主题,并使用每个主题运行测试。它还可以为您提供一些性能优势,因为您可以在构建服务器上并行化测试,因为每个测试步骤都是不同的测试步骤,并且每个测试步骤可以在不同的计算机上独立运行。

对于开发人员,您只能一次测试一个主题,并且必须手动更改config / env变量以更改为不同的主题,这可能并不理想。

其他人可能会更好地了解如何以您想象的方式做到这一点,手指交叉