Selenium Java Testng - 与页面工厂并行测试执行?

时间:2017-09-29 11:38:14

标签: java selenium testng

顺序测试通过,但在不同浏览器上多次运行时并行失败。我在这里做错了什么???

我尝试使用threadlocal,但它对我不起作用。我检查了驱动程序对象哈希码,并且所有测试都有唯一的线程ID和驱动程序对象哈希码,直到driver.get(url),当页面对象方法启动时,所有测试都变得混乱。 我尝试使用maven threadpoolsize并行运行但结果相同。

我被困住了。任何帮助表示赞赏。无法找到一个明确解释与页面工厂模式并行执行的单一博客。使用测试方法本身编写的所有selenium操作的简单脚本可以正常工作,但是当您必须使用页面工厂进行代码重用和维护时,这些脚本工作正常。

假设多个测试类和多个页面在实际场景中

System.out.println(fmt.format(test)); // 8:00am
    public class WebDriverManager {

        private static HashMap<Long, WebDriver> map = new HashMap<>();

        public static WebDriver getDriverInstance() {
            return map.get(Thread.currentThread().getId());
        }

        public static void startDriver(String OS_Browser) throws Exception {
            WebDriver driver;

            if (OS_Browser.equalsIgnoreCase("Win7_Chrome"))
                ChromeDriverManager.getInstance().version("2.31").setup();
                driver = new ChromeDriver();
                map.put(Thread.currentThread().getId(), driver);
        }

        public static void stopDriver() {
            WebDriver driver = map.get(Thread.currentThread().getId());
            if (null != driver) {
                driver.quit();
                driver = null;
            }
        }
    }

<!-- language: java -->

public class Login extends TestBase {

    @Parameters({"OS_Browser"})
    @BeforeMethod(alwaysRun = true)
    protected void oneTimeSetup(@Optional("Win7_Chrome") String OS_Browser) throws Exception {
        WebDriverManager.startDriver(OS_Browser);
    }

    @AfterMethod(alwaysRun = true)
    protected void oneTimeTeardown() {
        WebDriverManager.stopDriver();
    }


    @Test()
    public void Test_Error_Messages_For_Blank_Invalid_Incorrect_login_Credentials() throws Exception {
        WebDriver driver = WebDriverManager.getDriverInstance();
        driver.get(url);

        Login_Page LoginPage = new Login_Page(driver);

        LoginPage.wait_For_Login_PageLoad()
                // UI Validation
                .verify_Color_On_Links()
                .verify_Texts_On_Login_Page()
                .verify_Header_Logos()
                // Check error messages for blank email and password
                .click_SignIn()
                .wait_For_Blank_Credentials_Error_Message()
                // Check error messages for invalid email and password
                .login("admin", "admin")
                .wait_For_Invalid_Credentials_Error_Message()
                // Check error messages for incorrect email and password
                .login("admin@xyz.com", "admin")
                .wait_For_Incorrect_Credentials_Error_Message();
    }
}
public class Login_Page {

    private final WebDriver driver;

    @FindBy(css = "input#email")
    private WebElement login_username;
    @FindBy(css = "input#password")
    private WebElement login_password;
    @FindBy(css = "button#signin")
    private WebElement button_signIn;

    @FindBy(css = "a[ng-click*='terms']")
    private WebElement terms_of_use;
    @FindBy(css = "a[ng-click*='privacy']")
    private WebElement privacy_policy;
    @FindBy(css = "a[ng-click*='trademarks']")
    private WebElement trademarks;
    @FindBy(css = "a[ng-click*='ContactUs']")
    private WebElement contactus;

    @FindBy(css = "fieldset > div:nth-of-type(1) > p")
    private WebElement signin_text;
    @FindBy(css = ".container.ng-scope>img")
    private WebElement header_logo;

    @FindBy(css = "p[ng-show*='error.email'][aria-hidden='false']")
    private WebElement enter_valid_email_address_error_message;
    @FindBy(css = "p[ng-show*='loginform.emailaddress.$error.required'][aria-hidden='false']")
    private WebElement email_required_error_message;
    @FindBy(css = "p[ng-show*='loginform.password.$error.required'][aria-hidden='false']")
    private WebElement password_required_error_message;
    @FindBy(css = "span[translate='sign_error_msg']")
    private WebElement enter_correct_credentials;

    @FindBy(css = "img[src='img/common/ajax_progress.gif']")
    private WebElement pleaseWait;

    // Constructor takes a reference to the driver
    public Login_Page(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    public Login_Page click_SignIn() {
        button_signIn.click();
        return this;
    }

    public Login_Page login(String username, String password) {
        login_username.clear();
        login_username.sendKeys(username);

        login_password.clear();
        login_password.sendKeys(password);

        button_signIn.click();

        (new WebDriverWait(driver, 10))
                .until(ExpectedConditions.invisibilityOf(pleaseWait));
        return this;
    }

    public Login_Page wait_For_Blank_Credentials_Error_Message() throws InterruptedException {
        (new WebDriverWait(driver, 10)).until(ExpectedConditions.and(
                ExpectedConditions.visibilityOf(password_required_error_message),
                ExpectedConditions.visibilityOf(email_required_error_message)));

        String emailRequiredErrorMessage = email_required_error_message.getText().trim();
        Assert.assertEquals(emailRequiredErrorMessage, "Your email address is required to proceed.");

        String passwordRequiredErrorMessage = password_required_error_message.getText().trim();
        Assert.assertEquals(passwordRequiredErrorMessage, "Your password is required to proceed.");

        return this;
    }

    public Login_Page wait_For_Invalid_Credentials_Error_Message() throws InterruptedException {
        (new WebDriverWait(driver, 10))
                .until(ExpectedConditions.visibilityOf(enter_valid_email_address_error_message));

        String ErrorMessage = enter_valid_email_address_error_message.getText().trim();
        Assert.assertEquals(ErrorMessage, "Please enter the email address in a valid format.");

        return this;
    }

    public void wait_For_Incorrect_Credentials_Error_Message() {
        (new WebDriverWait(driver, 10))
                .until(ExpectedConditions.visibilityOf(enter_correct_credentials));
        String ErrorMessage = enter_correct_credentials.getText().trim();
        Assert.assertEquals(ErrorMessage, "Sign-in credentials did not match. If you have not created an account with your email address and a password, you will need to do so before signing in.");
    }

    public Login_Page verify_Color_On_Links() {
        String blue_Link_Color = "#026091";
        List<WebElement> allLinks = driver.findElements(By.cssSelector("body a[href='#']"));
        for (WebElement ele : allLinks) {
            String color = ele.getCssValue("color");
            String colorInHex = Color.fromString(color).asHex();
            assertEquals(colorInHex, blue_Link_Color);
        }
        return this;
    }

    public Login_Page verify_Texts_On_Login_Page() {
        String actual1 = signin_text.getText().trim();
        String expected1 = "Please enter your email address and password in order to proceed.";
        Assert.assertTrue(actual1.contains(expected1));
        return this;
    }

    public Login_Page verify_Header_Logos() {
        int headerLogoWidth = header_logo.getSize().getWidth();
        int headerLogoHeight = header_logo.getSize().getHeight();
        Assert.assertEquals(headerLogoWidth, 480);
        Assert.assertEquals(headerLogoHeight, 100);
        return this;
    }

    public Login_Page wait_For_Login_PageLoad() {
        JSWaiter.waitJQueryAngular();
        (new WebDriverWait(driver, 15)).until(ExpectedConditions.and(
                ExpectedConditions.visibilityOf(login_username),
                ExpectedConditions.visibilityOf(login_password),
                ExpectedConditions.visibilityOf(button_signIn)));
        return this;
    }

}

1 个答案:

答案 0 :(得分:1)

问题在于您的测试代码。您的代码在@BeforeTest带注释的方法中包含webdriver创建逻辑。

TestNG每@BeforeTest个代码仅调用一次<test>方法。

您的代码进一步继续将webdriver实例持久保存到其键为线程ID的映射中。

此模型适用于顺序执行。但是当你转向并行执行模型的那一刻,你很快就会看到问题,因为TestNG并不能保证它会运行@BeforeTest并且@Test方法会在同一个线程中运行。

作为第一步,您可能希望开始使用@BeforeMethod而不是使用@BeforeTest注释。然后,您可以将webdriver实例持久保存到ThreadLocal,因为TestNG会执行@BeforeMethod&gt; @Test&gt; @AfterMethod总是在同一个帖子上。

另一个选项基本上是将webdriver实例化和清理转移到一个监听器,你可以将它附加到你的测试类。这样您就可以坚持分离关注点,并且您的测试类不会受到webdriver生命周期管理逻辑的污染。

您可以在我的博客帖子here中阅读有关如何使用侦听器管理TestNG中的并行执行的更多信息。