I have a class named test.cs:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using qa.WrapperFactory;
namespace Common.PageObjects
{
public class Test
{
[FindsBy(How = How.XPath, Using = "xpath")]
private IWebElement foundElement;
[FindsBy(How = How.XPath, Using = "xpath")]
private IWebElement EnvironmentLogoElement;
[FindsBy(How = How.XPath, Using = "xpath")]
private IWebElement UsernameElement;
[FindsBy(How = How.Id, Using = "xpath")]
private IWebElement PasswordElement;
public void Setup()
{
// Set window to full screen
BrowserFactory.Driver.Manage().Window.Maximize();
// Clear all cookies
BrowserFactory.Driver.Manage().Cookies.DeleteAllCookies();
}
public void CheckLoginPage ()
{
WaitMethods.WaitForShort(() => foundElement.Displayed);
Assert.IsTrue(UsernameElement.Displayed);
Assert.IsTrue(PasswordElement.Displayed);
}
}
}
I want to call the method public void CheckLoginPage () from the specflow steps. that looks like this:
using System.Configuration;
using Common.PageObjects;
using qa.WrapperFactory;
using TechTalk.SpecFlow;
namespace RegressionTest
{
[Binding]
public class SmokeTestSteps
{
[Given(#"I go to the HRControlnet login page")]
public void GivenIGoToTheHRControlnetLoginPage()
{
BrowserFactory.InitBrowser("Firefox");
var subDomain = ConfigurationManager.AppSettings["Environment"];
BrowserFactory.LoadApplication(subDomain);
}
[Then(#"the result should be on the screen")]
public void ThenTheResultShouldBeOnTheScreen()
{
Test.CheckLoginPage();
}
}
}
I get now the error on the step ThenTheResultShouldBeOnTheScreen() with
Error CS0120 An object reference is required for the non-static field, method, or property.
I tried to make CheckLoginPage () a static but then all the xpaths give an error.
Anyone can help me out on how to fix this?
You just have to initialize the class and call the method something like below:
public void ThenTheResultShouldBeOnTheScreen()
{
new Test().CheckLoginPage();
}
public void ThenTheResultShouldBeOnTheScreen()
{
Test test = new Test() // initialize new instance of class
test.CheckLoginPage() // call method
}
If this doesn't work, you need to add reference
Related
Setup
DotNetCore in Visual Studio 2019
Selenium
SpecFlow
xUnit
I would like to implement the Page Object Model (POM) without the PageFactory
This is the code I have so far
POM
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
namespace Vasool.DrPay.Test
{
class LoginPage
{
private readonly IWebDriver _driver;
private const string PageUri = #"https://site.site/SignIn";
[FindsBy(How = How.Name, Using = "Input.Username")]
private IWebElement _username;
[FindsBy(How = How.Name, Using = "Input.Password")]
private IWebElement _password;
[FindsBy(How = How.Id, Using = "Input_RememberMe")]
private IWebElement _rememberLogin;
[FindsBy(How = How.CssSelector, Using = ".btn.btn-primary")]
private IWebElement _login;
public LoginPage(IWebDriver driver)
{
_driver = driver;
}
public static LoginPage NavigateTo(IWebDriver driver)
{
driver.Navigate().GoToUrl(PageUri);
return new LoginPage(driver);
}
public string Username { set { _username.SendKeys(value); } }
public string Password { set { _password.SendKeys(value); } }
public void Remember() { _rememberLogin.Click(); }
public void Login()
{
_login.Click();
}
}
}
Feature Step
using OpenQA.Selenium;
using TechTalk.SpecFlow;
using Xunit;
namespace Inc.Test
{
[Binding]
public class LoginSteps
{
private readonly IWebDriver _webDriver;
private LoginPage _loginPage;
public LoginSteps(ScenarioContext scenarioContext)
{
_webDriver = scenarioContext["WEB_DRIVER"] as IWebDriver;
}
[Given(#"I am on the Login Page")]
public void GivenIAmOnTheLoginPage()
{
_loginPage = LoginPage.NavigateTo(_webDriver);
}
[Given(#"I have entered the ""(.*)"" as the username")]
public void GivenIHaveEnteredTheAsTheUsername(string username)
{
_loginPage.Username = username;
}
[Given(#"I have entered the ""(.*)"" as the password")]
public void GivenIHaveEnteredTheAsThePassword(string password)
{
_loginPage.Password = password;
}
[Then(#"I should be navigated to the Home Page")]
public void ThenIShouldBeNavigatedToTheHomePage()
{
// ScenarioContext.Current.Pending();
}
}
}
When I try to run the tests under debugging, I get a null reference for _username
It might be that I am missing something very obvious as I am getting back to programming after a long time
You need to call PageFactory.InitElements(this, driver); in the constructor of LoginPage.
As you have noted in your comment, the PageFactory class is deprecated. Since C# supports properties and expression bodied members, the page factory has fallen out of favor. You end up with less code. For instance, the username property:
[FindsBy(How = How.Name, Using = "Input.Username")]
private IWebElement _username;
Would be rewritten as follows:
private IWebElement Username => driver.FindElement(By.Name("Input.Username"));
I'm new to mobile automation, and I'm facing a problem with page object pattern. When I try to find element with FindElementById everything works, here is my class with pop:
public class SamplePage
{
private AndroidDriver<AndroidElement> _driver;
[FindsByAndroidUIAutomator(ID = "com.miui.calculator:id/btn_1_s")]
private readonly AndroidElement _buttonOne;
[FindsByAndroidUIAutomator(ID = "android:id/button1")]
private readonly AndroidElement _confirmButton;
public SamplePage(AndroidDriver<AndroidElement> driver)
{
_driver = driver;
PageFactory.InitElements(_driver, this);
}
public void ClickOnConfirmButton()
{
//AndroidElement _confirmButton = _driver.FindElementById("android:id/button1");
_confirmButton.Click();
}
public void ClickOnButtonOne()
{
//AndroidElement _buttonOne = _driver.FindElementById("com.miui.calculator:id/btn_1_s");
_buttonOne.Click();
}
}
And here is main class
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Remote;
using System;
using AppiumDotNetSamples.Helper;
namespace AppiumDotNetSamples
{
[TestFixture()]
public class AndroidBasicInteractionsTest
{
private AndroidDriver<AndroidElement> driver;
private SamplePage _samplePage;
[SetUp()]
public void BeforeAll()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability(MobileCapabilityType.PlatformName, "Android");
capabilities.SetCapability(MobileCapabilityType.PlatformVersion, "7.1.2");
capabilities.SetCapability(MobileCapabilityType.AutomationName, "UIAutomator2");
capabilities.SetCapability(MobileCapabilityType.DeviceName, "3e52f2ee7d34");
capabilities.SetCapability("appPackage", "com.miui.calculator");
capabilities.SetCapability("appActivity", "com.miui.calculator.cal.CalculatorActivity");
driver = new AndroidDriver<AndroidElement>(new Uri("http://localhost:4723/wd/hub"), capabilities, TimeSpan.FromSeconds(180));
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
_samplePage = new SamplePage(driver);
}
[Test()]
public void Click()
{
_samplePage.ClickOnConfirmButton();
_samplePage.ClickOnButtonOne();
}
[TearDown()]
public void AfterAll()
{
driver.Quit();
}
}
}
What am I doing wrong? I test on on Xiaomi Calculator app, but earlier I got the same issues on any other app like Google Calculator.
The ClickConfirmButton method is not returning the driver and hence it is null.
You may want to try something similar this and see if it is working
public AboutPage goToAboutPage()
{
about.Click();
return new AboutPage(driver);
}
I've updated packages from my project on Mac and find out that PageObjects has been moved to SeleniumExtras. I struggle to use it but the error "System.NullReferenceException : Object reference not set to an instance of an object." is shown.
Nevertheless, the same element can be found through FindElement. Currently, I run out of ideas and would appreciate the help.
Thanks!
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NUnit.Framework;
using NUnit.Compatibility;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using SeleniumExtras.PageObjects;
namespace SomeTest
{
[TestFixture]
public class UnitTest1
{
[FindsBy(How = How.XPath, Using = "//*[#id='lst-ib']")]
public IWebElement _searchField;
public IWebDriver chrome;
[SetUp]
public void TestMethod1()
{
chrome = new ChromeDriver(#"/Volumes/Macintosh HD/QA/SomeTest/SomeTest/bin/Debug/netcoreapp2.1");
}
[Test]
public void SomeTest()
{
chrome.Navigate().GoToUrl("https://www.google.com/");
_searchField.SendKeys("Some Text");
}
[TearDown]
public void CloseBrowser()
{
chrome.Close();
}
}
}
Got solution:
Add all DotNetSeleniumExtras through NuGet Packages: PageObjects and PageObjects.Core;
Add namespaces:
using FindsByAttribute = SeleniumExtras.PageObjects.FindsByAttribute;
using How = SeleniumExtras.PageObjects.How;
I've written basic code which goes to a specific URL then changes the country and currency. I now want to tidy my code up and make it easier to maintain.
I've not used page object model before so have been reading about it. I understand the basic concepts of creating another class within the project and creating an extension to page objects that are used to initialize the web elements.
So essentially all my FindElements will be stored in a separate class. I've created two Classes within a PageObjects folder. HomePage and PreferencePage.
Below code is working and does exactly as I expect.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Interactions;
using System.Threading;
namespace Exercise1
{
class Program
{
static void Main(string[] args)
{
IWebDriver webDriver = new ChromeDriver();
webDriver.Navigate().GoToUrl("http://www.asos.com/men/");
webDriver.Manage().Window.Maximize();
webDriver.FindElement(By.XPath(".//button[#data-testid='country-selector-btn']")).Click();
WebDriverWait wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(5));
IWebElement country = wait.Until(ExpectedConditions.ElementExists(By.Id("country")));
SelectElementFromDropDown(country, "India");
IWebElement currency = wait.Until(ExpectedConditions.ElementExists(By.Id("currency")));
SelectElementFromDropDown(currency, "$ USD");
webDriver.FindElement(By.XPath(".//button[#data-testid='save-country-button']")).Click();
webDriver.Quit();
}
private static void SelectElementFromDropDown(IWebElement ele, string text)
{
SelectElement select = new SelectElement(ele);
select.SelectByText(text);
}
}
}
Now I want to implement Page Object Model. I have created my main TestClass, along with a HomePage and PreferencePage class. I'm unsure of how to implement the next bit. I know my TestClass will be making a call to the HomePage for the URL and PreferencePage for the country and currency but I'm unsure how the implementation would look.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
namespace Exercise1
{
class TestClass
{
private IWebDriver driver;
public void SetUp()
{
IWebDriver webDriver = new ChromeDriver();
webDriver.Manage().Window.Maximize();
}
public void ChangePreferences()
{
Homepage home = new Homepage(driver);
home.goToPage();
PreferencePage preference = home.GoToPreferencePage();
UpdateCountry = preference.UpdateCountryPreference();
UpdateCurrency = preference.UpdateCurrencyPreference();
UpdatePreference = preference.PreferenceUpdated();
}
}
}
HomePage class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise1
{
class HomePage
{
private IWebDriver driver;
public HomePage(IWebDriver driver)
{
this.driver = driver;
PageFactory.InitElements(driver, this);
}
public void goToPage()
{
driver.Navigate().GoToUrl("http://www.asos.com/men/");
}
}
}
Preference page
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise1.PageObjects
{
class PreferencePage
{
private IWebDriver driver;
public UpdateCountry(string text)
{
WebDriverWait wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(5));
IWebElement country = wait.Until(ExpectedConditions.ElementExists(By.Id("country")));
SelectElementFromDropDown(country, "India");
}
public void UpdateCurrency()
{
IWebElement currency = wait.Until(ExpectedConditions.ElementExists(By.Id("currency")));
SelectElementFromDropDown(currency, "$ USD");
}
public void ClickPreference()
{
webDriver.FindElement(By.XPath(".//button[#data-testid='save-country-button']")).Click();
}
}
}
it would look like that:
class TestClass
{
private IWebDriver driver;
public void SetUp()
{
..
}
public TestChangePreferences()
{
//actual test
ChangePreferences();
}
public void ChangePreferences()
{
.......
}
}
I'd wrap it with a testing framework such as NUnit for better test management and tools.
this could give you a pretty good example:
https://github.com/nunit/nunit-csharp-samples/blob/master/DataDrivenTests/GenericTestFixture.cs
I have rearranged my class files and Webdriver variable so I can use Context Injection to share the Webdriver variable between steps. Before I was using SetupFixture, Setup and TearDown which is not correct for BDD. Trying Context Injection now with BeforeAllTests, BeforeTestRun etc.
I am getting the error:
System.NullReferenceException: Object reference not set to an instance of an object
The line highlighted where the error is here:
[BeforeScenario]
public void RunBeforeScenario()
{
objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
}
My setup.cs implementation is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TechTalk.SpecFlow;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Firefox;
using SearchTest.Setup;
using BoDi;
using SearchTest.WebDriver;
namespace SearchTest.Setup
{
[Binding]
public class BeforeAllTests
{
private readonly IObjectContainer objectContainer;
private static SeleniumContext seleniumContext;
public BeforeAllTests(IObjectContainer container)
{
this.objectContainer = objectContainer;
}
[BeforeTestRun]
public static void RunBeforeAllTests()
{
seleniumContext = new SeleniumContext();
}
[BeforeScenario]
public void RunBeforeScenario()
{
objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
}
}
}
My HomePage class HomePage.cs implementation is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Firefox;
using NUnit.Framework;
using SearchTest.Setup;
using SearchTest.WebDriver;
using TechTalk.SpecFlow;
using BoDi;
namespace SearchTest.PageObjects
{
[Binding]
public class HomePage : PageObjectBase
{
private SeleniumContext seleniumContext;
//private IWebDriver driver{ get; set; }
[FindsBy(How = How.XPath, Using = ".//TITLE")]
public IWebElement Title{ get; set; }
// search text field on the homepage
//[FindsBy(How= How.Id, Using="twotabsearchtextbox")]
//private IWebElement Searchfield_ID { get; set; }
[FindsBy(How = How.XPath, Using = ".//*[#id='twotabsearchtextbox']")]
private IWebElement Searchfield_XPATH { get; set; }
[FindsBy(How = How.Id, Using = "nav-search-submit-text")]
private IWebElement SearchButton { get; set; }
[FindsBy(How = How.XPath, Using = ".//*[#id='nav-search']/form/div[2]/div/input")]
private IWebElement searchButton_Xpath {get; set;}
// public HomePage(IWebDriver driver)
public HomePage(SeleniumContext seleniumContext)
/*This is to reference the PageObjectBase Class. Passing in the Title of the
* page that is expected for the HomePage to ensure the correct Page is loaded
* before starting any tests.
*/
//: base("Amazon.co.uk: Low Prices in Electronics, Books, Sports Equipment & more")
: base(seleniumContext)
{
//driver = new FirefoxDriver();
//Console.Out.WriteLine("from Homepage Constructor Driver.title in SearchResultsPage class = " + driver.Title);
//driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5)); // Set implicit wait timeouts to 5 secs
//PageFactory.InitElements(driver, this);
this.seleniumContext = seleniumContext;
PageFactory.InitElements(seleniumContext.driver, this);
}
public void goToURL() {
//driver.Navigate().GoToUrl("http://www.amazon.co.uk");
}
public void EnterSearchText(String text)
{
Searchfield_XPATH.SendKeys(text);
}
public SearchResultsPage click_search_button() {
searchButton_Xpath.Click();
return new SearchResultsPage(seleniumContext);
}
}
}
Steps class SearchSteps.cs implementation is:
using System;
using TechTalk.SpecFlow;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using NUnit.Framework;
using SearchTest.PageObjects;
using SearchTest.WebDriver;
namespace SearchTest
{
[Binding]
public class SearchSteps
{
private SeleniumContext seleniumContext;
private IWebDriver driver { get; set; }
PageObjects.HomePage home_page { get; set; }
private SearchResultsPage search_results_page;
[Given(#"I navigate to the page ""(.*)""")]
public void GivenINavigateToThePage(string p0)
home_page = new PageObjects.HomePage(seleniumContext);
//home_page.goToURL();
}
[Given(#"I see the page is loaded")]
public void GivenISeeThePageIsLoaded()
{
//Assert.AreEqual("http://localhost:8080: PS4 products", driver.Title);
}
[When(#"I enter Search Keyword in the Search Text box")]
public void WhenIEnterSearchKeywordInTheSearchTextBox(Table table)
{
//string search_text = table.Rows[0]["Keyword"].ToString();
//driver.FindElement(By.Id("twotabsearchtextbox")).SendKeys(search_text);
//SearchResultsPage SearchResultsPage = home_page.EnterSearchText("F1");
home_page.EnterSearchText("F1");
}
[When(#"I click on Search Button")]
public void WhenIClickOnSearchButton()
{
//driver.FindElement(By.Name("BtnG")).Click();
search_results_page = home_page.click_search_button();
}
[Then(#"Search items shows the items related to PS4")]
public void ThenSearchItemsShowsTheItemsRelatedToPS4()
{
//Assert.AreEqual("PS4", driver.FindElement(By.XPath(".//*[#id='desktop-auto-sparkle-multi']/div/a")).Text);
//Assert.AreEqual("PS4", driver.FindElement(By.XPath("//h2[contains(text(), "PS4")]")));
search_results_page.get_search_result_title();
}
}
}
How do I resolve this please?
I think it is complaining that objectContainer.RegisterInstanceAs does not have any value, it is Null. It has not been instantiated?
Thanks, Riaz
your problem is that you have a typo in your constructor. This:
public BeforeAllTests(IObjectContainer container)
{
this.objectContainer = objectContainer;
}
should be
public BeforeAllTests(IObjectContainer container)
{
this.objectContainer = container;
}
you are not using the container instance given in the constructor, you are simply setting the objectContainer to itself.