从另一个功能中重新规划和重复使用步骤

时间:2013-10-04 11:55:22

标签: code-reuse specflow

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

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

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

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

不使用场景上下文,如何重用初始化类成员的其他功能文件中的步骤?

修改

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

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

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

非常感谢。

4 个答案:

答案 0 :(得分:17)

这里的重点是步骤Binding是全局的。 这似乎是许多人经历的Specflow的常见反模式。最初,您有一个阶段,即创建与您的要素文件匹配的绑定类的层次结构。相反,您需要创建与功能不匹配的协作类,而是通过协作生成功能。

就像你的主要应用程序代码一样。您不会有一个ATMMachineCashWithdrawal课程,而是拥有ATMMachinePINCodeCheckOperationSelectionWithdrawalOperation。这些对象协作使您的“我想提取现金”功能,当您添加“检查我的余额”功能时,您可以重用除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/

答案 1 :(得分:4)

我遇到了同样的问题。你需要设置属性" Binding"对于派生类,并为每个类设置范围。

我们假设您有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
{

}

答案 2 :(得分:0)

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

这使您可以将相关内容保存在自己的文件中,但仍然可以为重复使用灯具代码提供大量选项。

e.g。 (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 ...
    }
}

答案 3 :(得分: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);
         }