可以在步骤之间共享SpecFlow功能吗?

时间:2012-02-21 16:36:12

标签: c# testing bdd specflow

我们有从三个不同角度测试的代码:

  • 内部(直接致电)
  • 网络服务
  • 网络应用

由于我们不想三次编写功能文件,似乎我们应该在Steps之间共享功能,但除了可能在VS项目之间共享文件之外,我无法弄清楚这是如何完成的。总是看起来有点,好吧,片状。

在项目之间共享功能文件是实现这一目标的最佳方式,还是有更合理的方法?

6 个答案:

答案 0 :(得分:5)

我知道这是很久以前的问题,但我认为你想要的是可能的,并且有很多方法可以实现它,正如我所理解的那样。实际上,您真正想要的是定义一次功能,但根据您是在调用内部服务,公共Web服务还是WebApp,切换为该功能调用的步骤。讨论了在mailing list for specflow上解决此问题的各种方法,但其主要内容如下(示例显示了API与UI方法,但同样适用于您的内部vs Web服务与Web应用相比):

选项1#

(感谢邮件列表中的Oliver Friedrich

您需要为每个要为步骤改变的选项创建一个程序集,因此一个用于内部,一个用于Web服务,一个用于webapp。您告诉配置中有关所有不同程序集的specflow,如下所示:

<specFlow>
  <stepAssemblies>
    <stepAssembly assembly="Tests.API" />
    <stepAssembly assembly="Tests.UI" />
  </stepAssemblies>
</specFlow>

然后在您的常见测试步骤中,您有一些[BeforeTestRun]步骤选择从哪个程序集加载步骤:

[Binding]
public class TestRunSetup {

    // this method needs to be static
    [BeforeTestRun]
    public static void BeforeTestRun() 
    {
        if (RunApiTests()) // <-- implement this method to choose whether to run the tests via your chosen method 
        {
            Assembly.Load("Tests.API");
        }
        else 
        {
            Assembly.Load("Tests.UI");
        }
    }
}

选项2 ##

(感谢邮件列表中的Gáspár Nagy

试试generating the tests during the build。我不确定这是如何运作的,但这是我们可以研究的一个领域。

选项3 ##

(感谢邮件列表中的Dan Mork

如果您使用SpecFlow的依赖注入功能,那么您可以创建一个接口来执行您想要执行的调用,并在一组通用步骤中使用它。然后,您可以拥有该接口的3个实现,一个调用您的内部服务,一个调用Web服务,另一个调用Web应用程序。剩下的就是将该接口的正确实现注入到specflow步骤文件中,可以这样做:

// the abstract concept of the system that could be implemented with 
Selenium, HttpClient, etc. 
public interface IDocument 
{ 
    string Title { get;} 
    void Load(); 
} 

// the steps that are executed when the scenarios from your feature 
file are executed 
[Binding] 
public class Steps 
{ 
    private readonly IDocument _document; 

    public Steps(IDocument document) 
    { 
        _document = document; 
    } 

    [Given("something")] 
    public void GivenSomething() 
    { 
        // set up given state 
    } 

    [When("I view the document")] 
    public void WhenIViewTheDocument() 
    { 
        _document.Load(); 
    } 

    [Then(@"the title should be ""(.*)""")] 
    public void Then(string title) 
    { 
        Assert.ArEqual(_document.Title, title); 
    } 
} 

// this is where the magic happens - get the dependency injection 
// container and register an IDocument implementation
[Binding] 
public class Dependencies 
{ 
    private readonly IObjectContainer _objectContainer; 

    public Dependencies(IObjectContainer objectContainer) 
    { 
        _objectContainer = objectContainer; 
    } 

    [BeforeScenario] 
    public void RegisterDocumentInterfaces() 
    { 
        // register the correct IDocument implementation - UI or API 
    } 
} 

这仍然让你知道如何知道哪个 实施注册。这将取决于你的具体情况 解决方案,构建环境,测试执行环境, 等等的一些选择......

  • 标记参数BeforeScenarioAttribute
  • 为您的项目创建两个不同的构建配置 每个都定义不同的常量并使用预编译器指令 在代码中构建正确的注册
  • 添加条件逻辑以检查环境变量
  • 添加条件逻辑以检查配置设置
  • 我还没有检查过,但也许ScopeAttribute可以扩展 你要创建一个子类并提供自己的逻辑 执行BeforeScenarioAttribute方法。

希望这会为你提供一些你想做的选择。

恕我直言,第一个选项可能是最好的,但选项3也很可爱。

答案 1 :(得分:2)

我们有类似的情况,相同的功能必须适用于不同的输入方法。 我们的解决方案类似于Sam Holder的答案中的选项#3,但我们希望使用配置文件而不是代码来决定在每种情况下使用哪种实现。

这可以通过我们自己的DI机制来实现。除了能够在运行时注册实现之外,它还在首次调用时读取配置文件并加载所描述的类。

现在,对于与另一个共享功能的每个项目,我们都有一个.config文件,其中我们说明了每个共享接口的正确实现(主要是输入数据)。

这种方法的局限性在于您只能使用无参数构造函数来配置类,但对我们来说,它还不是问题。

如果您对该解决方案感兴趣,我会将其上传到github。

答案 2 :(得分:1)

对我们有用的东西,类似于Sam Holder 1st选项。

  1. 在VS项目中引用两个依赖项,以便将两个dll都复制到输出(编译)目录中。
  2. 为您的不同环境使用不同的app.config文件。
  3. 在每个app.config中配置权限stepassemblies(注释/删除不需要的那个)

    <specFlow>
      <stepAssemblies>
        <stepAssembly assembly="Tests.API" />
        <!--stepAssembly assembly="Tests.UI" /-->
      </stepAssemblies>
    </specFlow>
    
  4. 我不需要手动Assembly.Load调用,因为声明stepAssembly会将对NUnit的引用作为bindingAssembly传递(请参阅here)。

    如果我将两个程序集声明为stepAssemblies,我会遇到“模糊引用”异常。

    希望这会有所帮助,因为共享功能非常有用,如果没有必要(例如,当您升级软件并保持功能场景测试时)。

答案 3 :(得分:0)

我认为这些是3种不同的测试,所以我应该有3套功能文件。但是,如果你真的不想沿着这条路走下去那么你可以做一些事情,例如指定一个例子表。

Scenario Outline: Testing app

Given I have performed a call to my service using <application>
When I do something
Then this happens

Examples:
|Application|
| web service |
| web application |
| direct call |

上述情况将运行3次,传递3个值。该值将传递给给定的方法(在本例中),因此您可以使用它来设置一些上下文,然后重用相同的步骤定义文件。然后,每个步骤都会知道它是否意味着使用Web服务,Web应用程序或直接调用。

我仍然对这样做有所保留,因为它们是单独的测试,但它是你想要实现的目标。

答案 4 :(得分:0)

我不确定我是否正在考虑你想要什么,但这可能就是你想要的:

Scenario Outline: Multiple approaches to test same code
    Given I am using <ApproachToCallCode>
    When I do something
    Then I expect this result

Scenarios: Approaches
    |ApproachToCallCode|
    |Internal          |
    |WebService        |
    |WebApp            |

然后,您可以在给定方法中使用条件?我不确定这是否是最佳方法,但它应该有效。

答案 5 :(得分:0)

步骤不绑定到某个功能,如果步骤相同或至少是其中一些步骤,只要措辞相同或者您为步骤定义方法添加了额外的属性,那么它们可以在其他方案中重复使用#39; s或功能。

根据具体情况,我会将这些内容写成额外的场景或功能。