简体   繁体   English

Specflow-无法在功能文件之间共享步骤

[英]Specflow - Unable to share step between feature files

I'm a Specflow newbie who is just getting started, so hoping someone can help me? 我是一个Specflow新手,刚刚开始,所以希望有人可以帮助我吗? This could all be down to my own misunderstanding of how it should work, but hopefully someone can help me progress. 这可能是我自己对应该如何工作的误解,但是希望有人可以帮助我进步。

So! 所以! I have a test project I am working on that consists of 2 feature files and 2 Step class files, (and Pagefiles using POM). 我有一个正在处理的测试项目,包括2个功能文件和2个Step类文件(以及使用POM的Pagefile)。

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

The feature files both share one step, 'And the browser is closed', to close the browser at the end of each scenario. 功能文件都共享一个步骤,“并且浏览器已关闭”,以在每种情况下关闭浏览器。 This works for the Scenario tests in the Login.feature file, but it does not work for the Scenario test in the other feature file, ServiceSchedules.feature, which fails on this step, using 'Driver.Dispose'. 这适用于Login.feature文件中的方案测试,但不适用于其他功能文件ServiceSchedules.feature中的方案测试,该步骤在此步骤中使用“ Driver.Dispose”失败。 It returns the following error; 它返回以下错误;

System.NullReferenceException : Object reference not set to an instance of an object. System.NullReferenceException:对象引用未设置为对象的实例。

I expected that the step could be shared across feature files? 我希望该步骤可以在功能文件之间共享? (maybe not the way I am trying to do it?) (也许不是我尝试的方式?)

As a side note, I appreciate this is not perhaps best practice as I understand you can use hooks, but I ran into another issue when I tried using an AfterScenario Hook to try and close the browser after each test. 顺便说一句,我理解这不是最佳实践,因为我了解您可以使用挂钩,但是当我尝试使用AfterScenario Hook尝试在每次测试后关闭浏览器时遇到了另一个问题。 It returned another error (using the same code as below), when running the same Scenario test from the ServiceSchedules.feature file of; 当从的ServiceSchedules.feature文件运行相同的方案测试时,它返回了另一个错误(使用下面的相同代码)。

System.ArgumentException : The SearchContext of the locator object cannot be null Parameter name: locator System.ArgumentException:定位器对象的SearchContext不能为null参数名称:locator

But back to my current issue - here are my files; 回到我当前的问题-这是我的文件; FeatureFile 1: LoginFeature FeatureFile 1:LoginFeature

@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 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 Feature 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 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);
        }
    }
}

Hopefully I have given enough to go on - everything else works fine, just cant understand why the 'And the browser is closed' step from the LoginPageSteps.cs won't share itself with the scenario/test in the ServiceSchedules.feature file. 希望我有足够的机会继续工作-一切正常,只是无法理解为什么LoginPageSteps.cs中的“关闭浏览器”步骤不会与ServiceSchedules.feature文件中的场景/测试共享自己。 I am thinking that it could be something to do with way, and where I am first initializing the driver in the first place? 我认为这可能与方法有关,并且我首先在哪里初始化驱动程序? not sure how I could do that differently.. 不知道我该怎么做。

I did find I was able to create a new step 'And the browser closes' in the ServiceSchedules.feature and ServiceSchedulesSteps.cs file, and that works fine, but that of course means I have two steps that are the same, and they should really be shared from one place and file - which is how I thought it should all work! 我确实发现我能够在ServiceSchedules.feature和ServiceSchedulesSteps.cs文件中创建一个新步骤,然后“浏览器关闭”,并且工作正常,但这当然意味着我有两个相同的步骤,它们应该真正可以从一个位置共享一个文件-这就是我认为它应该全部正常工作的方式!

Really appreciate any help on this - and my apologies of it is something obvious and silly that I have missed or have not understood - I am learning and relatively new to coding :) 非常感谢您提供的任何帮助-我对此深表歉意,这是我已经错过或没有理解的明显而愚蠢的事情-我正在学习并且对编码还比较陌生:)

Cheers 干杯

The key moment in your case is that when thread comes to execute And the browser is closed from ServiceScheduleLoginPageContent scenario. 您遇到的关键时刻是何时执行线程, And the browser is closedServiceScheduleLoginPageContent场景And the browser is closed In that situation thread will go into step method (which definition is inside LoginPageSteps class), and in moment you try to call Driver.Dispose(); 在这种情况下,线程将进入step方法(其定义位于LoginPageSteps类内部),此刻,您尝试调用Driver.Dispose(); you get ERROR. 您得到错误。 Its because you use uninitialized DRIVER variable. 这是因为您使用了未初始化的DRIVER变量。 It never gets initialized since your execution process starts in ServiceSchedulesSteps class. 由于您的执行过程是在ServiceSchedulesSteps类中启动的,因此它永远不会初始化。 You have initialized Driver variable but in class ServiceSchedulesSteps . 您已经初始化了Driver变量,但是在class ServiceSchedulesSteps

I would say that the same cause of unitialized variable is might be the case with [AfterScenario]. 我想说,[AfterScenario]可能是导致统一变量的原因。

This happens because your step And the browser is closed has same syntax in 2 different .feature files. 发生这种情况的原因是您的“ And the browser is closed步骤在2个不同的.feature文件中具有相同的语法。 When you have same steps across different .feature files specFlow will recognize that inside your project there already exists defined step method for your step (that is because of [Binding] attribute you set for your step classes). 当您在不同的.feature文件中具有相同的步骤时,specFlow将识别出在您的项目内部已经为您的步骤定义了步骤方法(这是因为您为步骤类设置了[Binding]属性)。

Everybody would recommend you to read about Context Injection topic of spec flow. 每个人都建议您阅读有关规范流的上下文注入主题。 It is allowing sharing data between different step.cs files. 它允许在不同的step.cs文件之间共享数据。 But you should consider how you would handle it if your parameters for sharing are increasing with more scenarios or features. 但是,如果随着更多场景或功能的增加,用于共享的参数不断增加,则应考虑如何处理。 You just google it and you will find in documentation or in some blog. 您只要在Google上搜索它,就可以在文档或某些博客中找到它。

You can also stick to easy solution, to rename one of the And the browser is closed step and let generate new method so you have 2 step methods, keep all steps from one feature file in one step.cs file and avoid these kind of problems. 您也可以坚持使用简单的解决方案,重命名之一, And the browser is closed并生成新方法,因此您有2步方法,将一个功能文件中的所有步骤保留在一个step.cs文件中,避免了此类问题。 For now, consider how your scenarios/features might change, but don't bother too much you will handle it over time. 现在,请考虑您的方案/功能可能会如何变化,但不要太在意您会随着时间的推移而处理它。 But Context Injection is definitely worth trying it. 但是上下文注入绝对值得尝试。

Check it out: In SpecFlow how can I share data between steps/features? 检查一下: 在SpecFlow中,如何在步骤/功能之间共享数据?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM