Specflow NoSuchElementException By.XPath 找不到元素

Specflow NoSuchElementException Could not find element by By.XPath

我重新整理了我的 class 文件和 Webdriver 变量,这样我就可以使用上下文注入在步骤之间共享 Webdriver 变量。 在我使用不适合 BDD 的 SetupFixture、Setup 和 TearDown 之前。现在尝试使用 BeforeAllTests、BeforeTestRun 等进行上下文注入。 我使用 XPATH 的 FindElement 不工作。在我开始重新排列我的 class 文件之前它曾经工作过。
我收到错误:

OpenQA.Selenium.NoSuchElementException : Could not find element by: By.XPath: .//*[@id='twotabsearchtextbox']

错误所在的行突出显示:

Searchfield_XPATH.SendKeys(text);

XPath 定位器定义为:

[FindsBy(How = How.XPath, Using = ".//*[@id='twotabsearchtextbox']")]
private IWebElement Searchfield_XPATH { get; set; }

我的代码片段如下:

//class setup.cs:
namespace SearchTest.Setup
{
    [Binding]
    public class BeforeAllTests
    {
        private readonly IObjectContainer objectContainer;
        private static SeleniumContext seleniumContext;

        public BeforeAllTests(IObjectContainer container)
        {
            this.objectContainer = container;
        }

        [BeforeTestRun]
        public static void RunBeforeAllTests()
        {
            seleniumContext = new SeleniumContext();
            seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");
        }

        [BeforeScenario]
        public void RunBeforeScenario()
        {
            objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
        }
    }
}

// Class SeleniumContext.cs
namespace SearchTest.WebDriver
{
    public class SeleniumContext
    {
        public IWebDriver driver { get; private set; } 

        public SeleniumContext() 
        { 
            //create the selenium context
            driver = new FirefoxDriver();
     }
    }
}

//Class HomePage.cs
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) : base(seleniumContext)
        {
            //driver1 = new FirefoxDriver();
           //Console.Out.WriteLine(driver1.Title);
            //driver1.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5)); // Set implicit wait timeouts to 5 secs
            //PageFactory.InitElements(driver1, this);
            this.seleniumContext = seleniumContext;
            PageFactory.InitElements(seleniumContext.driver, this);
        }

    public void goToURL() {
        //driver = new FirefoxDriver();
        //driver1.Navigate().GoToUrl("http://localhost:8080");
    }

    public void EnterSearchText(String text)
        {
            Searchfield_XPATH.SendKeys(text);
        }

    public SearchResultsPage click_search_button() {
        searchButton_Xpath.Click();
        return new SearchResultsPage(seleniumContext);
    }

    }   
}

//Class PageObjectBase.cs
namespace SearchTest.PageObjects
{
    public class PageObjectBase
    {
        private SeleniumContext seleniumContext;
        //private IWebDriver Driver { get; set; }

        public PageObjectBase(SeleniumContext seleniumContext)
        {
            this.seleniumContext = seleniumContext;
            //Driver = driver;
            //Driver = new FirefoxDriver();
            //Console.Out.WriteLine("From base class Driver.title = " + Driver.Title);
            //if (Driver.Title != titleOfPage)
            //    throw new NoSuchWindowException("PageObjectBase: The Page Title doesnt match.");
        }
    }
}

//The HTML is:

<form class="nav-searchbar" role="search" name="site-search" method="GET" action="/s/ref=nb_sb_noss" accept-charset="utf-8">
    <div class="nav-left">
    <div class="nav-right">
    <div class="nav-fill">
    <div class="nav-search-field">
        <input id="twotabsearchtextbox" class="nav-input" type="text" tabindex="21" autocomplete="off" name="field-keywords" value=""/>
    </div>
    <div id="nav-iss-attach"/>
</div>
</form>

我该如何解决这个问题? 我是 BDD 的新手,如果您发现我的设置有任何问题并想建议我如何改进它,请这样做。

由于添加了以下代码:

[BeforeScenario]
    public void RunBeforeScenario()
    {
        objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
        // HERE INSTEAD
        seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");
    }

我得到的错误是:

System.ArguementNullException: Value cannot be Null. 

突出显示的行在这里:objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);

我的代码是:

public class BeforeAllTests
{
    private readonly IObjectContainer objectContainer;
    private static SeleniumContext seleniumContext;

    public BeforeAllTests(IObjectContainer container)
    {
        this.objectContainer = container;
    }

    //[BeforeTestRun]
   // public static void RunBeforeAllTests()
   // {
   //     seleniumContext = new SeleniumContext();
   //     seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");
   // }

    //[BeforeScenario]
    //public void RunBeforeScenario()
   // {
   //     objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
   //}

    [BeforeScenario]
    public void RunBeforeScenario()
    {
        objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
        // HERE INSTEAD
        seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");
    }
}

}

我猜你不想要这条线

seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");

在您标记为 [BeforeTestRun] 的方法中,因为这只会导航到那个 URL 一次,然后所有测试都是 运行(而不是在每个测试之前)。

将它放在标记为 [BeforeScenario] 的方法中可能更有意义,因为这会在每个场景执行之前将您导航到您的基地 URL。这更有可能发生,因为场景不应该相互依赖(因此,一个场景不应该依赖于另一个已经导航到特定页面的场景)。

[BeforeScenario]
public void RunBeforeScenario()
{
    objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
    // HERE INSTEAD
    seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");
}

这可以解释为什么 Selenium 无法找到该特定元素,因为当步骤定义为 运行.

时,它实际上可能不在正确的页面上

编辑:

所以 BeforeAllTests 的最终代码将是:

public class BeforeAllTests
{
    private readonly IObjectContainer objectContainer;
    private static SeleniumContext seleniumContext;

    public BeforeAllTests(IObjectContainer container)
    {
        this.objectContainer = container;
    }

    [BeforeTestRun]
    public static void RunBeforeAllTests()
    {
        seleniumContext = new SeleniumContext();
    }

    [BeforeScenario]
    public void RunBeforeScenario()
    {
        objectContainer.RegisterInstanceAs<SeleniumContext>(seleniumContext);
        seleniumContext.driver.Navigate().GoToUrl("http://localhost:8080");
    }
}