I'm a Specflow newbie who is just getting started, so hoping someone can help me? 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).
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'. It returns the following error;
System.NullReferenceException : Object reference not set to an instance of an object.
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. It returned another error (using the same code as below), when running the same Scenario test from the ServiceSchedules.feature file of;
System.ArgumentException : The SearchContext of the locator object cannot be null Parameter name: locator
But back to my current issue - here are my files; 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
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
@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);
}
}
}
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. 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!
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