访问助手类中的实例变量

时间:2016-01-01 23:19:00

标签: java

免责声明:我来自动态语言背景(Ruby),我试图提升我的Java技能。我担心我的问题,我在Ruby的背景下思考太多,并且会很感激一些指示。

问题:

我想看看如何让helper类知道WebDriver实例的存在而不必一直传递实例。我试图避免这种情况的原因是因为它会导致额外的方法参数和不那么流畅的测试写作。

上下文

我有一个测试用例(LoginIT.java),如下所示:

package com.testerstories.learning.symbiote;

import static com.testerstories.learning.helpers.Selenium.*;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class LoginIT {
  WebDriver driver;

  @BeforeTest
  public void startBrowser() {
    driver = new FirefoxDriver();
  }

  @AfterTest
  public void quitBrowser() {
    driver.quit();
  }

  @Test
  public void loginAsAdmin() {
    driver.get("http://localhost:9292");

    withElement("open").click();

    waitForPresence("username");

    withElement("username").sendKeys("admin");
    withElement("password").sendKeys("admin");
    withElement("login-button").submit();

    waitForPresence("notice");

    assertThat(withElement("notice", "className"), equalTo("You are now logged in as admin."));
  }
}

此处需要注意的关键方法是调用withElementwaitForPresence。这些是在我创建的Selenium类中定义的,并通过顶部的静态导入引用。

以下是Selenium.java,其中包含该逻辑:

package com.testerstories.learning.helpers;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class Selenium {
  public static WebElement withElement(String identifier, String locator) {
    switch (locator) {
        case "className":
            return driver.findElement(By.className(identifier));
        default:
            return withElement(identifier);
    }
  }

  public static WebElement withElement(String identifier) {
    return driver.findElement(By.id(identifier));
  }

  public static void waitForPresence(String identifier, String locator) {
    WebDriverWait wait = new WebDriverWait(driver, 10, 500);

    switch (locator) {
        case "className":
            wait.until(ExpectedConditions.visibilityOfElementLocated(By.className(identifier)));
        default:
            waitForPresence(identifier);
    }
  }

  public static void waitForPresence(String identifier) {
    WebDriverWait wait = new WebDriverWait(driver, 10, 500);
      wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(identifier)));

  }
}

这里的关键问题是引用driver的行。在LoginIT.java中定义这些方法时,这不是一个问题,因为在driver实例中定义了WebDriver。

我知道我的问题的一个答案是我可以将WebDriver实例传递给每个方法。因此,例如,我可以将此方法签名与withElement

一起使用
public static WebElement withElement(WebDriver driver, String identifier)

我添加另一个参数来传递WebDriver实例。但这意味着我的测试逻辑必须如下所示:

withElement(driver, "username").sendKeys("admin");
withElement(driver, "password").sendKeys("admin");
withElement(driver, "login-button").submit();

不是世界末日,但它不那么流利,似乎应该有更好的方式。

问题:

有更好的方法吗?

或者我实际上是在正确的轨道上并且应该接受如果我希望帮助方法与测试分开,那么必须传递driver引用?因此接受稍微冗长的测试陈述?

其他想法:

我能立刻想到的另一件事就是我创建了一个仅代表WebDriver本身的第三个类。然后我的测试(LoginIt.java)和我的测试助手(Selenium.java)使用第三个类。我不确定如何最好地实现这一点,但我也不确定走这条路是否有意义。

我之所以这么说,因为看起来如果我这样做,我只是创建一个类来包装WebDriver实例的创建。所以我必须创建一个类,然后获取该类的实例,以便我可以创建WebDriver的实例。似乎我增加了复杂性......但也许不是。因此我寻求指针。

2 个答案:

答案 0 :(得分:1)

另一种方法是允许初始化您的Selenium类。您可以在类构造函数中使用WebDriver引用,而不是使方法成为静态。

package com.testerstories.learning.helpers;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class Selenium {
    private final WebDriver driver;
    public Selenium(WebDriver webDriver) {
        this.driver = webDriver;
    }

    public WebElement withElement(String identifier, String locator) {
        switch (locator) {
            case "className":
                return driver.findElement(By.className(identifier));
            default:
                return withElement(identifier);
        }
    }

    public WebElement withElement(String identifier) {
        return driver.findElement(By.id(identifier));
    }

    public void waitForPresence(String identifier, String locator) {
        WebDriverWait wait = new WebDriverWait(driver, 10, 500);

        switch (locator) {
            case "className":
                wait.until(ExpectedConditions.visibilityOfElementLocated(By.className(identifier)));
            default:
                waitForPresence(identifier);
        }
    }

    public void waitForPresence(String identifier) {
        WebDriverWait wait = new WebDriverWait(driver, 10, 500);
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(identifier)));

    }
}

然后,您的LoginIT.java将使用@Before和驱动程序初始化引用,并将以前的静态调用更改为您站起来的实例:

package com.testerstories.learning.symbiote;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.testerstories.learning.helpers.Selenium;

public class LoginIT {
  WebDriver driver;
  Selenium selenium;

  @BeforeTest
  public void startBrowser() {
    driver = new FirefoxDriver();
    selenium = new Selenium(driver);
  }

  @AfterTest
  public void quitBrowser() {
    driver.quit();
  }

  @Test
  public void loginAsAdmin() {
    driver.get("http://localhost:9292");

    selenium.withElement("open").click();

    selenium.waitForPresence("username");

    selenium.withElement("username").sendKeys("admin");
    selenium.withElement("password").sendKeys("admin");
    selenium.withElement("login-button").submit();

    selenium.waitForPresence("notice");

    assertThat(selenium.withElement("notice", "className"), equalTo("You are now logged in as admin."));
  }
}

答案 1 :(得分:0)

我不完全确定这个解决方案是否符合最佳设计实践,但这就是我最终要做的事情。 (注意:我从TestNG更改为JUnit,但除此之外,一切都在功能上等同。)

我创建了一个Driver.java类:

package com.testerstories.learning.helpers;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class Driver {
    private WebDriver driver;

    public WebDriver getDriver() {
        if (null == driver) {
           driver = new FirefoxDriver();
        }
        return driver;
    }

    public void quitDriver() {
        if (null != driver) {
            driver.quit();
            driver = null;
        }
    }
}

因此,该类负责创建驱动程序实例并删除该实例。它在创建之前检查驱动程序是否为null,因此它不会继续创建新实例。在这里,我正在编写一个新的Firefox实例。最终我必须让这个类根据用户想要的东西创建新的浏览器驱动程序,但宝贝先步骤。

然后我创建了DriverFactory.java,看起来像这样:

package com.testerstories.learning.helpers;

import org.junit.After;
import org.junit.Before;
import org.openqa.selenium.WebDriver;

public class DriverFactory {
    private static Driver driver;

    @Before
    public void createDriver() {
        driver = new Driver();
    }

    public static WebDriver getDriver() {
        return driver.getDriver();
    }

    @After
    public void quitDriver() {
        driver.quitDriver();
    }
}

此工厂用于创建驱动程序实例。请注意@Before@After注释。所以这是测试必须依赖于驱动程序工厂的地方。所以这意味着我的测试现在继承了DriverFactory,其职责是提供一个实际的驱动程序实例。所以我的LoginIT.java测试现在改为此(继续使用Jeremiah提供的解决方案):

package com.testerstories.learning.symbiote;

import com.testerstories.learning.helpers.DriverFactory;
import com.testerstories.learning.helpers.Selenium;
import org.junit.Test;
import org.openqa.selenium.WebDriver;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class LoginIT extends DriverFactory {
    WebDriver driver;
    Selenium selenium;

    @Test
    public void loginAsAdmin() {
        driver = DriverFactory.getDriver();
        selenium = new Selenium(driver);

        driver.get("http://localhost:9292");

        selenium.withElement("open").click();

        selenium.waitForPresence("username");

        selenium.withElement("username").sendKeys("admin");
        selenium.withElement("password").sendKeys("admin");
        selenium.withElement("login-button").submit();

        selenium.waitForPresence("notice", "className");

        assertThat(selenium.withElement("notice", "className").getText(), equalTo("You are now logged in as admin."));
    }
}

我还在做一些(全部?)jpmc26关注的事情,我不能说老实说这样做。但这确实有效。

如果有人有任何想法 - 特别是批评者 - 我最愿意听到他们的意见。正如我在最初的问题中所提到的,作为一名Ruby程序员,我多年来一直试图在Java上做得更好。至少可以这么说,这一直很艰难。