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

4
我们有一些代码需要从三个不同的角度进行测试:
  • 内部调用
  • Web服务
  • Web应用程序
由于我们不想写三次功能文件,因此似乎应该在步骤之间共享功能,但是我无法想象如何实现它,除了可能在VS项目之间共享文件,但这似乎有点靠不住。
是否共享项目之间的功能文件是实现这一目标的最佳方法,还是存在更明智的方法?

1
只是一个想法...您可以在主代码上添加一些包装类,以便检测使用它的人的来源。然后保留统计数据。 - NoWar
6个回答

6
我知道这个问题已经问了很久,但我认为你想要的是可能的,并且有几种方法可以实现。实际上,您真正想要的是定义一次功能,但根据调用内部服务、公共Web服务或WebApp切换调用该功能的步骤。在specflow邮件列表中讨论了解决此问题的各种方法,但其要点如下所示(示例显示API与UI方法,但我认为对于您的内部与Web服务与Webapp也适用):

选项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)

尝试在构建期间 生成测试。我不确定这样做的确切方式,但这是我们可以研究的一个领域。

选项 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中添加tag参数
  • 为项目创建两个不同的构建配置,每个配置都定义不同的常量,并使用预编译器指令将正确的注册信息编译到代码中
  • 添加条件逻辑来检查环境变量
  • 添加条件逻辑来检查配置设置
  • 我没有检查过,但也许ScopeAttribute是可扩展的,您可以创建一个子类,并提供自己的逻辑来确定是否执行BeforeScenarioAttribute方法。
希望这些选项能够给您提供一些想法。
在我看来,第一个选项可能是最好的选择,尽管第三个选项也很棒。

@Oliver,感谢您的编辑 :),以及原始内容。 - Sam Holder

2
我们曾经遇到过类似的情况,需要相同的功能适用于不同的输入方式。我们的解决方案与Sam Holder的第三个选项类似,但我们希望使用配置文件而不是代码来决定每种情况下要使用哪个实现。
这可以通过自己的DI机制实现。除了能够在运行时注册实现之外,它还会在首次调用时读取配置文件并加载所描述的类。
现在,对于每个与另一个项目共享特性的项目,我们都有一个.config文件,在其中声明每个共享接口(主要是输入数据接口)的正确实现。
这种方法的限制是你只能配置没有参数的构造函数的类,但对我们来说,这还没有成为问题。
如果您对此解决方案感兴趣,我将在github上上传它。

1
我们的做法与Sam Holder的第一种选项类似,这对我们很有效。
  1. Reference both dependency in your VS project so that both dll will be copied in your output (compilation) directory.
  2. use different app.config files for your different environments.
  3. in every app.config configure the right stepassemblies (comment/delete the one not needed)

    <specFlow>
      <stepAssemblies>
        <stepAssembly assembly="Tests.API" />
        <!--stepAssembly assembly="Tests.UI" /-->
      </stepAssemblies>
    </specFlow>
    
我不需要手动调用Assembly.Load,因为声明一个stepAssembly将会将引用作为bindingAssembly传递给NUnit(请参见此处)。
如果我将两个程序集都声明为stepAssemblies,则会遇到“模糊引用”异常。
希望这有所帮助,因为共享功能非常有用,甚至是必要的(例如,在升级软件并保留功能场景测试时)。

0

我认为这实际上是三个不同的测试,因此在我的想法中应该有三组功能文件。但是,如果您真的不想走这条路,那么您可以做一些类似于指定示例表格的事情,例如:

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应用程序或直接调用。
我仍然对这样做有所保留,因为它们是单独的测试,但这是实现您想要达到的目的的一种方式。

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            |

那么,你可以在给定的方法中使用条件语句吗?我不确定这是否是最好的方法,但它应该能够工作。


0

步骤不限于特定功能,只要步骤相同或至少有一些相同的措辞,或者您在步骤定义方法中添加额外属性,那么它们可以在其他场景或功能中重复使用。

根据情况,我会将它们编写为额外的场景或功能。


问题是关于在步骤之间共享功能,而不是在功能之间共享步骤。 - Rik Hemsley
实际上执行的不是功能,而是它们的步骤。功能只是一个逻辑容器。 - Ryan Burnham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接