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);
}
Related
I am creating a framework to test a web app using Specflow and Nunit and I have run into an error that I previously did not have.
The error I get is: BoDi.ObjectContainerException: 'Interface cannot be resolved: OpenQA.Selenium.IWebDriver'
This originates in the base class when I try to resolve the _container into a IWebDriver.
I get no compile errors with this and the bug only shows up when the test is run.
BasePage.cs
using System;
using BoDi;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using ExpectedConditions = SeleniumExtras.WaitHelpers.ExpectedConditions;
namespace TriageManagerAutomation.BaseClasses
{
public class BasePage
{
public readonly IObjectContainer _container;
public static IWebDriver Driver { get; set; }
private readonly int timeOut = 20;
public BasePage(IObjectContainer container)
{
_container = container;
Driver = _container.Resolve<IWebDriver>();
}
}
}
LoginSteps.cs
using TechTalk.SpecFlow;
using NUnit.Framework;
using BoDi;
using TriageManagerAutomation.BaseClasses;
using TriageManagerAutomation.Login.PageObjects;
namespace TriageManagerAutomation.Login.TestSteps
{
[Binding]
public class LoginSteps : StepBase
{
private readonly IObjectContainer _container;
private readonly LoginPage loginPage;
public LoginSteps(IObjectContainer container) : base(container)
{
_container = container;
loginPage = new LoginPage(container);
}
[Given(#"I have logged in to Triage Manager using (.*) and (.*) as the login credentials")]
public void GivenIHaveLoggedInToTriageManager(string username, string password)
{
//Open the login page using chrome driver and submit login details
loginPage.Open();
Assert.IsTrue(loginPage.IsVisible, "Page Not Loaded");
loginPage.EnterDetailsAndSubmit(username, password);
}
[Then(#"I am successfully logged in")]
public void ThenIAmSuccessfullyLoggedIn()
{
loginPage.CheckLoggedIn();
}
[Given(#"I Logout")]
public void GivenILogout()
{
loginPage.Logout();
}
[BeforeScenario]
public void Startup()
{
RegisterChromeDriver();
_container.RegisterInstanceAs(_driver);
}
[AfterScenario]
public void Destroy()
{
Destory();
}
}
}
LoginPage.cs
using NUnit.Framework;
using BoDi;
using System.Threading;
using TriageManagerAutomation.BaseClasses;
using TriageManagerAutomation.Login.Locators;
using TriageManagerAutomation.Main.Locators;
namespace TriageManagerAutomation.Login.PageObjects
{
public class LoginPage : BasePage
{
private readonly LoginLocators loginLocators;
private readonly NavLocators navLocators;
public LoginPage(IObjectContainer container) : base(container)
{
loginLocators = new LoginLocators(container);
navLocators = new NavLocators(container);
}
public bool IsVisible
{
get
{
return Driver.Url.Contains("/icp-tm/autotest/login");
}
internal set { }
}
public void CheckLoggedIn()
{
Assert.IsTrue(IsElementVisible(navLocators.mainMenu), "Page failed to load");
}
internal void Open()
{
Driver.Manage().Window.Maximize();
Driver.Navigate().GoToUrl("https://tm26.kanayo.works/icp-tm/autotest/login");
}
internal void EnterDetailsAndSubmit(string username, string password)
{
loginLocators.Username.SendKeys(username);
loginLocators.Password.SendKeys(password);
loginLocators.LoginButton.Click();
}
public void Logout()
{
Driver.SwitchTo().DefaultContent();
loginLocators.LogoutButton.Click();
Thread.Sleep(1000);
Driver.SwitchTo().Alert().Accept();
}
}
}
You have a order problem.
SpecFlow creates an instance of the LoginSteps class to call the BeforeScenarioHook method Startup. But when it does that, it executed the constructor where you want to create an instance of the LoginPage, which want to resolve the WebDriver.
But at this point in time, the webdriver is not yet registered in the container and can't be resolved.
I would move the line loginPage = new LoginPage(container); into the hook to solve this problem.
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 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
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.
I have created two projects in MS Visual C# 2010 Express. The first project has a class SugarcrmLogin with the method TheSugarCrmLoginTest().
The second project has a class Sugarcrm with the method Main. This project has a reference to the project dll of the first project.
In the method Main I have instantiated the class SugarcrmLogin and called the SugarCrmLoginTest() method.
Here the code of the Main method in the second project, where I instantiate the class and call the method:
public static void Main()
{
SugarcrmLogin Login;
Login = new SugarcrmLogin();
Login.TheSugarcrmLoginTest();
}
Both projects build succesfully in MS Visual C#, but when I try to run the project dll of the second project with Nunit, I get the error 'Object reference not set to an instance of an object' with a reference to TheSugarCrmLoginTest() method in the first project.
First project
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Support.UI;
namespace SeleniumTests
{
[TestFixture]
public class SugarcrmLogin
{
private IWebDriver driver;
private StringBuilder verificationErrors;
private string baseURL;
private bool acceptNextAlert = true;
[SetUp]
public void SetupTest()
{
//driver = new FirefoxDriver();
driver = new InternetExplorerDriver();
baseURL = "http://127.0.0.1/";
verificationErrors = new StringBuilder();
}
[TearDown]
public void TeardownTest()
{
try
{
driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.AreEqual("", verificationErrors.ToString());
}
[Test]
public void TheSugarcrmLoginTest()
{
driver.Navigate().GoToUrl(baseURL + "/sugarcrm/index.php?module=Users&action=Login");
driver.FindElement(By.Id("user_name")).Clear();
driver.FindElement(By.Id("user_name")).SendKeys("admin");
driver.FindElement(By.Id("user_password")).Clear();
driver.FindElement(By.Id("user_password")).SendKeys("admin");
driver.FindElement(By.Id("login_button")).Click();
}
private bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
private bool IsAlertPresent()
{
try
{
driver.SwitchTo().Alert();
return true;
}
catch (NoAlertPresentException)
{
return false;
}
}
private string CloseAlertAndGetItsText()
{
try
{
IAlert alert = driver.SwitchTo().Alert();
string alertText = alert.Text;
if (acceptNextAlert)
{
alert.Accept();
}
else
{
alert.Dismiss();
}
return alertText;
}
finally
{
acceptNextAlert = true;
}
}
}
}
Second project:
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Support.UI;
namespace SeleniumTests
{
[TestFixture]
public class Sugarcrm
{
public IWebDriver driver;
private StringBuilder verificationErrors;
public string baseURL;
private bool acceptNextAlert = true;
[SetUp]
public void SetupTest()
{
//driver = new FirefoxDriver();
driver = new InternetExplorerDriver();
baseURL = "http://127.0.0.1/";
verificationErrors = new StringBuilder();
}
[TearDown]
public void TeardownTest()
{
try
{
driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.AreEqual("", verificationErrors.ToString());
}
[Test]
public static void Main()
{
SugarcrmLogin Login;
Login = new SugarcrmLogin();
Login.TheSugarcrmLoginTest();
}
private bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
private bool IsAlertPresent()
{
try
{
driver.SwitchTo().Alert();
return true;
}
catch (NoAlertPresentException)
{
return false;
}
}
private string CloseAlertAndGetItsText()
{
try
{
IAlert alert = driver.SwitchTo().Alert();
string alertText = alert.Text;
if (acceptNextAlert)
{
alert.Accept();
}
else
{
alert.Dismiss();
}
return alertText;
}
finally
{
acceptNextAlert = true;
}
}
}
}
With the edit that shows the code: driver is null. The constructor does not assign it, and you do not call the SetupTest() method which does.
Frankly, though, your Sugarcrm unit test shouldn't be instantiating and calling-into another (SugarcrmLogin) unit test like that, IMO. This seems like "using your unit test framework incorrectly".