15

我正在尝试学习specflow,现在。目前我有 2 个功能文件。

在第二个功能文件中,我重用了第一个功能文件中的一个步骤。

Specflow 自动识别第一个功能文件中的步骤,当 Specflow 为我的第二个功能生成步骤时,它很聪明,并且没有重新生成我正在重用的步骤。

但是这一步是一个 Given 步骤,它初始化了要素类的一个成员字段。

在不使用场景上下文的情况下,如何重用另一个功能文件中初始化类成员的步骤?

编辑

例如,如果您有一个在多个功能文件中使用的 Given 我已登录。此“给定”创建一个用户对象,该对象被记录并将其作为成员存储在 .cs 功能文件中。

当您在另一个 .feature 中使用相同的 Given 时,Specflow 不会在相应的 .cs 文件中重新生成它。当您调试正在使用它的场景时,它会从第一个 .cs 文件中执行它。

但我无法访问第一个 .cs 功能文件的成员。我打算使用静态成员,但也许还有另一种解决方案?

非常感谢。

4

4 回答 4

20

这里的重点是 StepBinding是全局的。这似乎是 Specflow 的常见反模式,很多人都经历过。最初,您需要创建与功能文件匹配的绑定类层次结构。相反,您需要创建与特性不匹配的协作类,而是通过它们的协作来产生特性。

它就像您的主要应用程序代码一样。你不会有一个单一的ATMMachineCashWithdrawal类,而是你会有一个ATMMachine, 有一个PINCodeCheck, anOperationSelection和 a WithdrawalOperation。这些对象协作使您的“我想提取现金”功能,当您添加“检查我的余额”功能时,您可以重用除WithdrawalOperation.

Specflow 中的绑定是相同的。我们可能有一个ATMTester知道如何设置ATMMachine和提供您的设备Given I have a cash machine full of cash,您可能有一个CustomerTester知道如何伪造/模拟/设置您的帐户余额的设备Given my account has loads of money in it

幸运的是,SpecFlow 也提供了协作类的方法。看看http://www.specflow.org/documentation/Sharing-Data-between-Bindings/

于 2013-10-07T09:16:37.957 回答
10

我只是有同样的问题。您需要为派生类设置属性“绑定”并为每个派生类设置范围。

假设您有 2 个功能:

  • 特征:我的第一个特征
  • 特点:我的第二个特点

    Feature: My First Feature
    Background: 
    Given a precondition
    When ...
    Then ...
    
    Feature: My Second Feature
    Background: 
    Given a precondition
    When ...
    Then ...
    

你有一个定义共享行为的 BaseClass

    // no Binding attribute for the BaseClass
    public class BaseClass
    {
        [Given(@"a precondition")]
        public void GivenAPrecondition()
        {
        }
    }

然后 2 个类定义了 2 个特性的行为

[Binding]
[Scope(Feature = "My First Feature")]
public class MyFirstFeature : BaseClass
{

}

[Binding]
[Scope(Feature = "My Second Feature")]
public class MySecondFeature : BaseClass
{

}
于 2017-01-27T10:38:10.283 回答
0

我做过的一件事是partial class在各种 *.cs 文件之间使用单一的、大量的拆分。

这使您可以将相关的东西分开保存在自己的文件中,但仍然为您提供了很多选项来重用您的夹具代码。

例如(Feature1Steps.cs)

namespace YourProject.Specs
{
    [Binding] // This can only be used once.
    public partial class YourProjectSpecSteps
    {
        // Feature 1 methods ...
    }
}

对于下一个功能(Feature2Steps.cs)

namespace YourProject.Specs
{
    public partial class YourProjectSpecSteps // same class, already bound
    {
        // Feature 2 methods ...
    }
}
于 2015-08-21T13:15:59.223 回答
0

我知道您提到您有两个功能文件,但您可能还想考虑创建一个功能文件,其中包含两个场景,其中场景使用一个通用步骤以及两个具有不同实现的同名步骤。(重载函数)

Ex:  Login.featue file

Feature: Login
    Test the login functionality of the application.
    Will verify if the username and password combination is working as expected.

Scenario: Verify if the login functionality is working
    Given I have navigated to the application
    # user name and password are hard coded
    When  I fill in my form
    | username       | password     |
    |name@xyz.com    | pwd          |
    .... 

Scenario: Verify if the login functionality is working for sql server data
    Given I have navigated to the application
    # gets the user name and password from the database
    When  I fill in my form  
    ....

 LoginSteps.cs file

   [Binding]
   public  class LoginSteps
    {

        [Given(@"I have navigated to the application")]
        public void GivenIHaveNavigatedToTheApplication()
        {
         // this code is used by both scenarios
         Browser.Navigate().GoToUrl(ConfigurationManager.AppSettings["TestUrl"]);
        }

        // hard coded username and password
        [When(@"I fill in my form")]
        public void WhenIFillInMyForm(Table table)
        {
            dynamic credentials = table.CreateDynamicInstance();
            LoginFunction(credentials.username, credentials.password);
        }

        // gets the username and password from the database
        [When(@"I fill in my form")]
        public void WhenIFillInMyForm()
        {
            string username = "";
            string password = "";
            string sql = <select statement>;

            using (SqlConnection connection = new SqlConnection())
            {
                connection.ConnectionString = ConfigurationManager.ConnectionStrings["SQLProvider"].ConnectionString;
                connection.Open();

                SqlCommand myCommand = new SqlCommand(sql, connection);

                using (SqlDataReader myDataReader = myCommand.ExecuteReader())
                {
                    while (myDataReader.Read())
                    {
                        username = myDataReader["name"].ToString();
                        password = myDataReader["pwd"].ToString();
                    }
                }
            }
           LoginFunction(username, password);
         }
于 2018-03-19T13:30:55.077 回答