Specflow - 无法在特征文件之间共享步骤

Specflow - Unable to share step between feature files

我是刚刚入门的Specflow新手,希望有人能帮助我?这可能都是我自己对它应该如何工作的误解,但希望有人能帮助我进步。

所以!我有一个正在处理的测试项目,它包含 2 个功能文件和 2 个步骤 class 文件(以及使用 POM 的页面文件)。

1.) ServiceSchedules.feature 2.) ServiceSchedulesSteps.cs 3.) Login.feature 4.) LoginPageSteps.cs

两个功能文件共享一个步骤,'And the browser is closed',即在每个场景结束时关闭浏览器。这适用于 Login.feature 文件中的场景测试,但不适用于另一个功能文件 ServiceSchedules.feature 中的场景测试,该文件使用 'Driver.Dispose' 在这一步失败。它returns下面的错误;

System.NullReferenceException : Object reference not set to an instance of an object.

我预计该步骤可以跨功能文件共享? (也许不是我想要的方式?)

附带说明一下,我知道这可能不是最佳实践,因为我知道您可以使用挂钩,但是当我尝试使用 AfterScenario Hook 尝试关闭浏览器时,我 运行 遇到了另一个问题每次测试。它返回另一个错误(使用与下面相同的代码),当 运行 来自 ServiceSchedules.feature 文件的相同场景测试时;

System.ArgumentException : The SearchContext of the locator object cannot be null Parameter name: locator

但是回到我当前的问题——这是我的文件; 特征文件 1:登录特征

@All @Login
Feature: LoginPage
    In order to use the Program
    As a User
    I want to be be able to see the login Page

Background:
    Given I want to login

@Content
Scenario: LoginPageContent
    When I view the Login Page
    Then everything I need to login is present
    And the browser is closed

@ErrorMessage
Scenario: InvalidLoginError
    When I enter invalid Username of "!£$%^&*^%?["
    And enter an invalid Password "£$%^&*£!"
    And the submit button is pressed
    Then an Invalid Name or Password error will display on a popup
    And the browser is closed

@ErrorMessage
Scenario: NoLoginDetailsError
    And I have not entered a username
    And I have not entered a password
    When the submit button is pressed
    Then an error message will display under the username field of "The User name field is required."
    And an error message will display under the password field of "The Password field is required."
    And the browser is closed

LoginPageSteps.cs

using NUnit.Framework;
using OpenQA.Selenium;
using System;
using System.Threading;
using TechTalk.SpecFlow;

namespace Selenium.Services.SpecFlow2.SpecFlow.CodedSteps
{
    [Binding]
    public class LoginPageSteps
    {
        public IWebDriver Driver { get; set; }

        [Given(@"I want to login")]
        public void GivenIWantToLogin()
        {
            Driver = UserActions.IntializeDriver();
        }

        [Given(@"I have not entered a username")]
        public void GivenIHaveNotEnteredAUsername()
        {
            var signinpage = new SignInPage(Driver);
            Thread.Sleep(10);
            signinpage.UserNameS.Clear();
        }

        [Given(@"I have not entered a password")]
        public void GivenIHaveNotEnteredAPassword()
        {
            var signinpage = new SignInPage(Driver);
            Thread.Sleep(10);
            signinpage.PasswordS.Clear();
        }

        [When(@"I view the Login Page")]
        public void WhenIViewTheLoginPage()
        {
            Thread.Sleep(2000);
            Assert.AreEqual(Driver.Url, "http://qa-URL-Ommitted");
        }

        [When(@"I enter invalid Username of ""(.*)""")]
        public void WhenIEnterInvalidUsernameOf(string invalidUsername)
        {
            var signinpage = new SignInPage(Driver);
            Thread.Sleep(10);
            signinpage.UserNameS.Clear();
            signinpage.UserNameS.SendKeys(invalidUsername);
        }

        [When(@"enter an invalid Password ""(.*)""")]
        public void WhenEnterAnInvalidPassword(string invalidPassword)
        {
            var signinpage = new SignInPage(Driver);
            Thread.Sleep(10);
            signinpage.PasswordS.Clear();
            signinpage.PasswordS.SendKeys(invalidPassword);
        }

        [When(@"the submit button is pressed")]
        public void WhenTheSubmitButtonIsPressed()
        {
            var signinpage = new SignInPage(Driver);

            signinpage.SubmitButtonS.Click();
        }

        [Then(@"everything I need to login is present")]
        public void ThenEverythingINeedToLoginIsPresent()
        {
            var UserActions = new UserActions(Driver);
            var signinpage = new SignInPage(Driver);

            UserActions.WaitForElement(Driver, signinpage.TimeServicesSignInTitle);
            UserActions.WaitForElement(Driver, signinpage.UserNameText);
            UserActions.WaitForElement(Driver, signinpage.PasswordText);
            UserActions.WaitForElement(Driver, signinpage.RememberMeText);
        }

        [Then(@"the browser is closed")]
        public void ThenTheBrowserIsClosed()
        {
            Driver.Dispose();
        }

        [Then(@"an Invalid Name or Password error will display on a popup")]
        public void ThenAnInvalidNameOrPasswordErrorWillDisplayOnAPopup()
        {
            var signinpage = new SignInPage(Driver);

            bool InvalidPopUp = signinpage.InvalidPasswordAndOrUserNameS.Displayed;
        }

        [Then(@"an error message will display under the username field of ""(.*)""")]
        public void ThenAnErrorMessageWillDisplayUnderTheUsernameFieldOf(string usernameError)
        {
            var signinpage = new SignInPage(Driver);
            var UserActions = new UserActions(Driver);

            UserActions.WaitForElement(Driver, signinpage.UserNameRequired);
            Assert.AreEqual(usernameError, signinpage.UserNameRequiredS.Text);
        }

        [Then(@"an error message will display under the password field of ""(.*)""")]
        public void ThenAnErrorMessageWillDisplayUnderThePasswordFieldOf(string passwordError)
        {
            var signinpage = new SignInPage(Driver);
            var UserActions = new UserActions(Driver);

            UserActions.WaitForElement(Driver, signinpage.PasswordRequired);
            Assert.AreEqual(passwordError, signinpage.PasswordRequiredS.Text);
        }
    }
}

FeatureFile 2:ServiceSchedule 功能

@All @ServiceSchedules
Feature: ServiceSchedules
    In order to use the Program
    As a User
    I want to be be able to easily Search, Create and Edit ServiceSchedules upon login, from the homepage

Background: 
    Given I have logged on

@Content
Scenario: ServiceScheduleLoginPageContent
    When I view the Homepage Page
    Then I am taken to the Dashboard ServiceSchedules homepage
    And the correct breadcrumb of "Dashboard" and "Serivce Schedules" are present
    And the Customer Details search options appear
    And the Export and Search buttons appear
    And the results pane appears
    And the results pane has a quick filter
    And the browser is closed

ServiceScheduleSteps.cs

using NUnit.Framework;
using OpenQA.Selenium;
using System;
using System.Threading;
using TechTalk.SpecFlow;
using Selenium.Services.SpecFlow2.Pages;

namespace Selenium.Services.SpecFlow2
{
    [Binding]
    public class ServiceSchedulesSteps
    {
        public IWebDriver Driver { get; private set; }

        [When(@"I view the Homepage Page")]
        public void WhenIViewTheHomepagePage()
        {
            Thread.Sleep(2000);
            Assert.AreEqual(Driver.Url, "http://qa-URL-Omitted");
        }

        [Given(@"I have logged on")]
        public void GivenIHaveLoggedOn()
        {
            Driver = UserActions.IntializeDriver();
            SignInPage signinpage = new SignInPage(Driver);

            UserActions.FillLogInField(Config.Credentials.Valid.Username, Config.Credentials.Valid.Password, Driver);
            signinpage.SubmitButtonS.Click();
            Thread.Sleep(1000);
        }

        [Then(@"I am taken to the Dashboard ServiceSchedules homepage")]
        public void ThenIAmTakenToTheDashboardServiceSchedulesHomepage()
        {
            var homePage = new HomePage(Driver);
            var UserActions = new UserActions(Driver);
            UserActions.WaitForElement(Driver, homePage.BreadCrumbServiceSchedules);
        }

        [Then(@"the top navigation is present")]
        public void ThenTheTopNavigationIsPresent()
        {
            var menu = new Menu(Driver);

            bool MenuAdmin = menu.Admin.Displayed;
            bool MenuWorkShop = menu.WorkShops.Displayed;
            bool MenuParts = menu.JobTemplate.Displayed;
            bool MenuSearch = menu.Search.Displayed;
        }

        [Then(@"the correct breadcrumb of ""(.*)"" and ""(.*)"" are present")]
        public void ThenTheCorrectBreadcrumbOfAndArePresent(string breadcrumb1, string breadcrumb2)
        {

            var homePage = new HomePage(Driver);
            var UserActions = new UserActions(Driver);

            UserActions.WaitForElement(Driver, homePage.BreadCrumbDashboard);
            Assert.AreEqual(breadcrumb1, homePage.BreadCrumbDashboardS.Text);

            UserActions.WaitForElement(Driver, homePage.BreadCrumbServiceSchedules);
            Assert.AreEqual(breadcrumb2, homePage.BreadCrumbServiceSchedulesS.Text);
        }

        [Then(@"the Customer Details search options appear")]
        public void ThenTheCustomerDetailsSearchOptionsAppear()
        {
            var homePage = new HomePage(Driver);
            var UserActions = new UserActions(Driver);

            //Pane Expand Button
            UserActions.WaitForElement(Driver, homePage.FilterSchedulesExpandButton);

            UserActions.WaitForElement(Driver, homePage.CustomerNameField);
            UserActions.WaitForElement(Driver, homePage.CustomerEmailField);
            UserActions.WaitForElement(Driver, homePage.CustomerPhoneField);
            UserActions.WaitForElement(Driver, homePage.CustomerPostcodeField);

        }
        [Then(@"the Export and Search buttons appear")]
        public void ThenTheExportAndSearchButtonsAppear()
        {
            var homePage = new HomePage(Driver);
            var UserActions = new UserActions(Driver);

            UserActions.WaitForElement(Driver, homePage.ExportButton);
            UserActions.WaitForElement(Driver, homePage.SearchButton);
        }

        [Then(@"the results pane appears")]
        public void ThenTheResultsPaneAppears()
        {
            var homePage = new HomePage(Driver);
            var UserActions = new UserActions(Driver);

            UserActions.WaitForElement(Driver, homePage.CustomerResultsTable);
            UserActions.WaitForElement(Driver, homePage.RTableCustomerNo);
            UserActions.WaitForElement(Driver, homePage.RTableCustomer);
            UserActions.WaitForElement(Driver, homePage.RTableWatch);
            UserActions.WaitForElement(Driver, homePage.RTableTotalCosts);
            UserActions.WaitForElement(Driver, homePage.RTableDates);
            UserActions.WaitForElement(Driver, homePage.RTableActions);
            UserActions.WaitForElement(Driver, homePage.Pagination);
        }

        [Then(@"the results pane has a quick filter")]
        public void ThenTheResultsPaneHasAQuickFilter()
        {
            var homePage = new HomePage(Driver);
            var UserActions = new UserActions(Driver);

            UserActions.WaitForElement(Driver, homePage.QuickFilter);
        }
    }
}

希望我已经付出了足够的努力继续 - 其他一切正常,只是无法理解为什么 LoginPageSteps.cs 中的 'And the browser is closed' 不会与 scenario/test 中的 scenario/test 共享ServiceSchedules.feature 文件。我在想这可能与方式有关,我首先在哪里初始化驱动程序?不确定我该怎么做。

我确实发现我能够在 ServiceSchedules.feature 和 ServiceSchedulesSteps.cs 文件中创建一个新步骤 'And the browser closes',并且工作正常,但这当然意味着我有两个步骤它们是一样的,它们真的应该从一个地方和一个文件共享——这就是我认为一切都应该有效的方式!

非常感谢对此提供的任何帮助 - 我对此表示歉意,这是我错过或不理解的明显而愚蠢的事情 - 我正在学习并且对编码相对较新:)

干杯

你的情况的关键时刻是当线程从 ServiceScheduleLoginPageContent 场景执行 And the browser is closed 时。在那种情况下,线程将进入 step 方法(定义在 LoginPageSteps class 内),并且在您尝试调用 Driver.Dispose(); 的那一刻,您会得到错误。这是因为您使用了未初始化的 DRIVER 变量。它永远不会被初始化,因为您的执行过程从 ServiceSchedulesSteps class 开始。您已初始化 Driver 变量,但在 class ServiceSchedulesSteps 中。

我想说的是 [AfterScenario] 可能也是导致单元化变量的原因。

发生这种情况是因为您的步骤 And the browser is closed 在 2 个不同的 .feature 文件中具有相同的语法。当您在不同的 .feature 文件中执行相同的步骤时,specFlow 会识别出在您的项目中已经存在为您的步骤定义的步骤方法(这是因为您为步骤 classes 设置的 [Binding] 属性)。

每个人都会推荐您阅读规范流的上下文注入主题。它允许在不同的 step.cs 文件之间共享数据。但是,如果您的共享参数随着更多场景或功能而增加,您应该考虑如何处理它。您只需 google 它,您就会在文档或某些博客中找到它。

您也可以坚持简单的解决方案,重命名 And the browser is closed 步骤之一并生成新方法,这样您就有了 2 个步骤方法,将一个特征文件中的所有步骤集中在一个 step.cs文件并避免此类问题。现在,请考虑您的 scenarios/features 可能会发生怎样的变化,但不要太在意,您会随着时间的推移处理它。但是上下文注入绝对值得一试。

查看:In SpecFlow how can I share data between steps/features?