1

I am receiving a error from a group of tests where it seems to be looking for an element listed in the Setup Method despite the method already being executed, the error being thrown is:

Message: OpenQA.Selenium.NoSuchElementException : no such element: Unable to locate element: {"method":"xpath","selector":"//input[@id='txtCompany']"}
(Session info: chrome=71.0.3578.98)
(Driver info: chromedriver=2.45.615291
(ec3682e3c9061c10f26ea9e5cdcf3c53f3f74387),platform=Windows NT 10.0.17763 x86_64)"

I have tried replacing and commenting out code, I have also added in the extra step of returning to the Splash screen before clicking on the Reports drop down though this has not fixed the problem. Similar code works correctly outside of this class where a different test is taking place.

using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using NUnit.Framework;


[SetUp]
public void initalise()
    {   
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        driver.Manage().Window.Maximize();

        //Navigates to the Test DB
        driver.Url = "https://TESTWEBSITE.co.uk";
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        //Find Company Text Box and send company name

driver.FindElement(By.XPath("//input[@id='txtCompany']")).SendKeys("COMPANY");

        //Find username Text Box and send username

driver.FindElement(By.XPath("//input[@id='txtUsername']")).SendKeys("6969_1");

        //Find password and send

driver.FindElement(By.XPath("//input[@id='txtPassword']")).SendKeys("PASSWORD");

        //Find Login button and click
        driver.FindElement(By.XPath("//input[@id='cmdLogin']")).Click();
    }

  [Test, Order(1)]
    public void reportsStandard()
    {
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        driver.FindElement(By.XPath("//span[@class='rpOut']//span[@class='rpText'][contains(text(),'Reports')]")).Click();
        driver.FindElement(By.XPath("//span[contains(text(),'Standard Reports')]")).Click();

        IWebElement ReportType = driver.FindElement(By.XPath("//div[@id='ctl00_ContentPlaceHolder_lstReports']//ul[@class='rlbList']"));
        Assert.AreEqual(true, ReportType.Displayed);

    }
   [Test, Order(2)]
  public void reportsPandLCustomer()
    {

        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        driver.FindElement(By.XPath("//span[contains(text(),'Home')]")).Click();
        driver.FindElement(By.XPath("//span[@class='rpOut']//span[@class='rpText'][contains(text(),'Reports')]")).Click();
        driver.FindElement(By.XPath("//span[contains(text(),'Profit and Loss by Customer')]")).Click();

        IWebElement AdvancedFiltering = driver.FindElement(By.XPath("//a[@id='ContentPlaceHolder_cmdAdvancedFiltering']"));
        Assert.AreEqual(true, AdvancedFiltering.Displayed);
    }

I would expect that the Tests execute as:

Setup (Launches Browser > Go to Website > Login)
Test Order 1 ( Click Reports Drop Down > Click Standard Report)
Test Order 2 ( Click Home Button > Click Reports Drop Down > ProfitAndLoss Button)

The actual result is:

Setup - Passes,
Test Order 1 - Passes,
Test Order 2 - Fails - Error is unable to locate an element which is only used during Setup Method.

waka
  • 3,362
  • 9
  • 35
  • 54
Jimmy
  • 85
  • 1
  • 11
  • After click on home button.Please share the html to find out the element.OP might help you who are online ATM. – KunduK Feb 11 '19 at 12:21
  • Hi, Element for Reports remains the same. Home button returns user to logged in splash page. There's a lot of HTML, is there anything in particular you are looking for? – Jimmy Feb 11 '19 at 12:35
  • I think it is failing to identify the element ```"//input[@id='txtCompany']"``` in the setup block.But you said Setup passed.Strange!!! – KunduK Feb 11 '19 at 13:46
  • I don't think that's the issue as what is happening is that it is passing the Setup block and is then logged in. Once logged in the setup block shouldn't apply as the login page is no longer present. It then executes Test 1 which is to open the reportsStandard which passes, but once that is finished it then loops back to the Setup block, rather than continuing onto Test, Order(2), reportsPandLCustomer. As It is looping back but not initialising a new session then it is searching for a txtCompany element which will only ever be present on the login page. – Jimmy Feb 11 '19 at 14:31
  • Just to add, if I change the Setup to Test, Order(1) and execute from there, it does work correctly. So it is an issue with the test runner going back to the Setup block. – Jimmy Feb 11 '19 at 14:35

2 Answers2

2

So after playing around and some googling If I change the [Setup] Method to [OneTimeSetup] for the Class Then this works correctly. New Code will look like:

[OneTimeSetUp]
    public void initalise()
    {  //Maximise Window
        driver.Manage().Window.Maximize();

        //Navigates to the NG Test DB
        driver.Url = "https://TESTWEBSITE.co.uk";
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        //Find Company Text Box and send company name
        driver.FindElement(By.XPath("//input[@id='txtCompany']")).SendKeys("CompanyName");

        //Find username Text Box and send username
        driver.FindElement(By.XPath("//input[@id='txtUsername']")).SendKeys("6969_1");

        //Find password and send
        driver.FindElement(By.XPath("//input[@id='txtPassword']")).SendKeys("Password!");

        //Find Login button and click
        driver.FindElement(By.XPath("//input[@id='cmdLogin']")).Click();
    }
 [Test, Order(1)]
    public void reportsStandard()
    {
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        driver.FindElement(By.XPath("//span[@class='rpOut']//span[@class='rpText'][contains(text(),'Reports')]")).Click();
        driver.FindElement(By.XPath("//span[contains(text(),'Standard Reports')]")).Click();
        IWebElement ReportType = driver.FindElement(By.XPath("//div[@id='ctl00_ContentPlaceHolder_lstReports']//ul[@class='rlbList']"));
//Assert.AreEqual(true, ReportType.Displayed);
    }
    [Test, Order(2)]
    public void reportsPandLCustomer()
    {

        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        driver.FindElement(By.XPath("//span[contains(text(),'Home')]")).Click();
        driver.FindElement(By.XPath("//span[@class='rpOut']//span[@class='rpText'][contains(text(),'Reports')]")).Click();
        driver.FindElement(By.XPath("//span[contains(text(),'Profit and Loss by Customer')]")).Click();

        IWebElement AdvancedFiltering = driver.FindElement(By.XPath("//a[@id='ContentPlaceHolder_cmdAdvancedFiltering']"));
        Assert.AreEqual(true, AdvancedFiltering.Displayed);
    }
    [Test, Order(3)]
    public void reportsPandLPhone()
    {
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
        driver.FindElement(By.XPath("//span[contains(text(),'Home')]")).Click();
        driver.FindElement(By.XPath("//span[@class='rpOut']//span[@class='rpText'][contains(text(),'Reports')]")).Click();
        driver.FindElement(By.XPath("//span[contains(text(),'Profit and Loss by Phone Number')]")).Click();
        IWebElement ResetBTN = driver.FindElement(By.XPath("//span[@id='ctl00_FunctionBarPlaceHolder_cmdReset']"));
        Assert.AreEqual(true, ResetBTN.Displayed);
    }
Jimmy
  • 85
  • 1
  • 11
  • 1
    You don't want to do this. This may make your current code work but it's not the way your tests should be structured. See my answer. – JeffC Feb 11 '19 at 14:55
  • Thanks for the help Jeff, follow up question if thats ok? you mention that a new instance of the browser should be launched for every test, would this not greatly increase the amount of time needed in order to execute the tests? – Jimmy Feb 11 '19 at 15:47
  • 1
    I wouldn't say "greatly" but it might add a few seconds to each test run. The idea is that you compensate for this extra time by running them in parallel which will more than make up for the small amount of extra time to launch the browser each time. In the end, it will help you more than it hurts you... even if you aren't running the tests in parallel. Having a clean run each time will make your tests more resilient and make debugging failures MUCH easier. – JeffC Feb 11 '19 at 22:19
  • Thanks for the feedback Jeff :) – Jimmy Feb 12 '19 at 08:54
2

The method tagged with [SetUp] is run before each test, see docs.

I think the problem is this:

  1. Setup runs, logging you in
  2. Test1 runs, passing
  3. Since Test1 has completed, Setup runs again but this time you are already logged in since you are reusing browser sessions (or at least appear to be given the code you have posted) so when the Setup method looks for the Company field it's not there.

Best practice is to use one browser session per test. It ensures that you have the cleanest run possible each time. You need to add launching the browser to your [SetUp] method and you need to add a [TearDown] method that quits the browser. This is how your tests should run:

  1. Setup runs, launches the browser and logs you in
  2. Test1 runs, passes
  3. TearDown runs and closes the browser
  4. Setup runs, launches the browser and logs you in
  5. Test2 runs, passes
  6. TearDown runs and closes the browser

Your TearDown method should be something like (see the docs linked above)

[TearDown]
public void Cleanup()
{
    driver.Quit();
}

Side note 1:

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

This does not actually wait... it sets the wait time for the driver instance. It only needs to be set once ever and shouldn't be used again unless you want to change the timeout to a different value. You can remove all the instances of this except for the first one that should be in your Setup method.

Side note 2: Selenium contributors have stated to avoid using ImplicitWait. You should instead use WebDriverWait.

Side note 3: Your tests should not be run in a particular order. Each test should be independent of each other and should be able to run in any order.

JeffC
  • 22,180
  • 5
  • 32
  • 55
  • Hey Jeff, by changing [Setup] to [OneTimeSetup] in each class this has resolved the issue, I posted that particular fix as an answer, thanks for the link to the Docs, they'll come in handy! I do have a teardown method as well though I wasn't getting that far in the code so didn't post it. I'll change the instance of the implict wait, didnt know about the WebDriverWait so thanks for that as well. In relation to the order, theres no requirement for them to execute this way, as other than logon they'll all execute fine, its just for ease of tracking as I'll have to share these amongst the team. – Jimmy Feb 11 '19 at 15:22