【问题标题】:Specflow - Unable to share step between feature filesSpecflow - 无法在功能文件之间共享步骤
【发布时间】:2018-11-01 11:09:58
【问题描述】:

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

所以!我正在处理一个测试项目,其中包含 2 个功能文件和 2 个 Step 类文件(以及使用 POM 的页面文件)。

1.) ServiceSchedules.feature 2.) ServiceSchedulesSteps.cs 3.) 登录功能 4.) LoginPageSteps.cs

两个功能文件共享一个步骤,“并且浏览器已关闭”,以在每个场景结束时关闭浏览器。这适用于 Login.feature 文件中的场景测试,但不适用于其他功能文件 ServiceSchedules.feature 中的场景测试,该文件在此步骤中使用“Driver.Dispose”失败。它返回以下错误;

System.NullReferenceException : 对象引用未设置为 对象的实例。

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

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

System.ArgumentException : 定位器对象的 SearchContext 不能为空参数名称:定位器

但回到我当前的问题 - 这是我的文件; 功能文件 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);
        }
    }
}

功能文件 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 中的“并且浏览器已关闭”步骤不会与 ServiceSchedules.feature 中的场景/测试共享文件。我在想这可能与方式有关,我首先在哪里初始化驱动程序?不知道我该如何做不同的事情..

我确实发现我能够在 ServiceSchedules.feature 和 ServiceSchedulesSteps.cs 文件中创建一个新步骤“浏览器关闭”,而且效果很好,但这当然意味着我有两个相同的步骤,他们真的应该从一个地方和文件共享 - 这就是我认为它应该全部工作的方式!

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

干杯

【问题讨论】:

    标签: c# selenium specflow


    【解决方案1】:

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

    我会说统一变量的相同原因可能是 [AfterScenario] 的情况。

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

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

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

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

    【讨论】:

    • 非常感谢您的确认,我认为问题是这样的,但很高兴得到知道的人的确认 :) 我在我一直在看的教程中简要阅读了有关上下文注入的内容,所以再看看!我确实有点天真地认为它会找到并运行那一步 - 但这当然太容易了!感谢您的帮助,不胜感激。
    • 设法使用上下文注入来做到这一点,谢谢!比预期的要容易。再次非常感谢您的帮助。
    猜你喜欢
    • 2021-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    • 2015-08-26
    • 2018-08-15
    • 1970-01-01
    相关资源
    最近更新 更多