Selenium+TestNG实战-5 基本框架实现二

在第四篇文章,我们初步实现了页面操作Action和测试脚本代码分层,而且还添加了日志打印的功能。这篇,我们继续来优化之前代码,逐步采用新的方式去慢慢实现我们POM框架的实现过程。本篇主要是两个问题,浏览器初始化代码的封装和调用,第二个就是页面对象类的基类的创建和封装常见的方法。

Selenium+TestNG实战-5 基本框架实现二

声明:本文由凯哥Java(www.kaigejava.com)发布在凯哥公众号(kaigejava)。

浏览器初始化代码封装

思路:

我们想实现一个浏览器引擎类,这个类决定了调用什么浏览器测试,并提前做一些操作,例如浏览器最大化窗口,时间等待,打开测试服务器的首页地址等。我们可以通过if语句或者switch语句来决定启动什么类型浏览器。所以,我们需要引入外部配置文件来控制浏览器得到的值从而决定启动哪个浏览器。

在Java中,我们一般喜欢把配置文件写到properties文件,我这里,把测试浏览器类型和测试服务器首页地址写入配置文件。

项目下新建一个TestConfig文件夹,新建一个config.properties文件,内容如下

# browser switcher

#browserName = Firefox

browserName = Chrome

#browserName = IE

# test server switcher

baseUrl = http://127.0.0.1/wordpress

#baseUrl = http://tx731324.wgidc.top/wordpress/

在myproject包下新建一个BrowserEngine.java文件,用来读取配置文件中浏览器类型和测试入口主地址的。

package myframework;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.Properties;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.ie.InternetExplorerDriver;

public class BrowserEngine {

public String browserName;

public String serverURL;

public WebDriver driver;

public void initConfigData() throws IOException{

Properties p = new Properties();

// 加载配置文件

InputStream ips = new FileInputStream(".\\TestConfig\\config.properties");

p.load(ips);

Logger.Output(LogType.LogTypeName.INFO, "Start to select browser name from properties file");

browserName=p.getProperty("browserName");

Logger.Output(LogType.LogTypeName.INFO, "Your had select test browser type is: "+ browserName);

serverURL = p.getProperty("baseUrl");

Logger.Output(LogType.LogTypeName.INFO, "The test server URL is: "+ serverURL);

ips.close();

}

public WebDriver getBrowser(){

if(browserName.equalsIgnoreCase("Firefox")){

System.setProperty("webdriver.gecko.driver", ".\\Tools\\geckodriver.exe");

driver = FirefoxDriver();

Logger.Output(LogType.LogTypeName.INFO, "Launching Firefox ...");

}else if(browserName.equalsIgnoreCase("Chrome")){

System.setProperty("webdriver.chrome.driver", ".\\Tools\\chromedriver.exe");

driver= new ChromeDriver();

Logger.Output(LogType.LogTypeName.INFO, "Launching Chrome ...");

}else if(browserName.equalsIgnoreCase("IE")){

System.setProperty("webdriver.ie.driver", ".\\Tools\\IEDriverServer.exe");

driver= new InternetExplorerDriver();

Logger.Output(LogType.LogTypeName.INFO, "Launching IE ...");

}

driver.get(serverURL);

Logger.Output(LogType.LogTypeName.INFO, "Open URL: "+ serverURL);

driver.manage().window().maximize();

Logger.Output(LogType.LogTypeName.INFO, "Maximize browser...");

callWait(5);

return driver;

}

private WebDriver FirefoxDriver() {

// TODO Auto-generated method stub

return null;

}

/*

* 关闭浏览器并退出方法

*/

public void tearDown() throws InterruptedException{

Logger.Output(LogType.LogTypeName.INFO, "Closing browser...");

driver.quit();

Thread.sleep(1000);

}

/*

* 隐式时间等待方法

*/

public void callWait(int time){

driver.manage().timeouts().implicitlyWait(time, TimeUnit.SECONDS);

Logger.Output(LogType.LogTypeName.INFO, "Wait for "+time+" seconds.");

}

}

在testsuites包下的homepage包新建一个测试类,先来测试下这个浏览器引擎类写的对不对。

package testsuites.homepage;

import java.io.IOException;

import org.openqa.selenium.WebDriver;

import myframework.BrowserEngine;

public class TestLogin {

public static void main(String args[]) throws IOException {

WebDriver driver = null;

BrowserEngine browser = new BrowserEngine();

browser.initConfigData();

driver = browser.getBrowser();

System.out.println(driver.getTitle());

}

}

运行完,如果可以启动火狐或者谷歌浏览器而且控制台可以看到日志信息,说明这个浏览器引擎类没有问题。到这里,我们完成了浏览器初始化代码提取出来,放到一个单独类供我们调用。接下来就是如何进一步实现POM。

页面对象基类创建过程

我们发现很多页面具有相同或者相似的属性或者操作方法,这个时候我们就把相似的东西提取出来,抽象化,进行编写一个类。这个过程就是典型的面向对象的思维。

在myframeword下新建一个BasePage.java,这里有一个建议,如果经常变更BasePage.java就把这个文件放到pageobjects包下,如果变更不是很多,这里我们先放myframework包下。

BasePage.java内容。

package myframework;

import java.util.Iterator;

import java.util.Set;

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

public class BasePage {

public static WebDriver driver;

public static String pageTitle;

public static String pageUrl;

/*

* 构造方法

*/

public BasePage () {

}

public BasePage (WebDriver driver){

BasePage.driver = driver;

}

/*

* 在文本框内输入字符

*/

protected void type(WebElement element , String text){

try {

if (element.isEnabled()) {

element.clear();

Logger.Output(LogType.LogTypeName.INFO, "Clean the value if any in "+ element.toString()+".");

element.sendKeys(text);

Logger.Output(LogType.LogTypeName.INFO, "Type value is: " + text+".");

}

} catch (Exception e) {

Logger.Output(LogType.LogTypeName.ERROR, e.getMessage()+".");

}

}

/*

* 点击元素,这里指点击鼠标左键

*/

protected void click(WebElement element){

try {

if (element.isEnabled()) {

element.click();

Logger.Output(LogType.LogTypeName.INFO, "Element: "+element.toString()+" was clicked.");

}

} catch (Exception e) {

Logger.Output(LogType.LogTypeName.ERROR, e.getMessage()+".");

}

}

/*

* 在文本输入框执行清除操作

*/

protected void clean(WebElement element){

try {

if (element.isEnabled()) {

element.clear();

Logger.Output(LogType.LogTypeName.INFO, "Element "+element.toString()+" was cleaned.");

}

} catch (Exception e) {

Logger.Output(LogType.LogTypeName.ERROR, e.getMessage()+".");

}

}

/*

* 判断一个页面元素是否显示在当前页面

*/  

protected void verifyElementIsPresent(WebElement element){

try {

if (element.isDisplayed()) {

Logger.Output(LogType.LogTypeName.INFO, "This Element " + element.toString().trim()+" is present.");

}

} catch (Exception e) {

Logger.Output(LogType.LogTypeName.ERROR, e.getMessage()+".");

}

}

/*

* 获取页面的标题

*/

protected String getCurrentPageTitle(){

pageTitle=driver.getTitle();

Logger.Output(LogType.LogTypeName.INFO, "Current page title is "+pageTitle);

return pageTitle;

}

/*

* 获取页面的url

*/

protected String getCurrentPageUrl(){

pageUrl=driver.getCurrentUrl();

Logger.Output(LogType.LogTypeName.INFO, "Current page title is "+pageUrl);

return pageUrl;

}

/*

* 处理多窗口之间切换

*/

public void switchWindow(){

String currentWindow = driver.getWindowHandle();// 获取当前窗口句柄  

Set<String> handles = driver.getWindowHandles();// 获取所有窗口句柄

Logger.Output(LogType.LogTypeName.INFO, "当前窗口数量: "+ handles.size());

Iterator<String> it = handles.iterator();

while (it.hasNext()) {

if (currentWindow == it.next()) {

continue;

}

try {

driver.close();

WebDriver window = driver.switchTo().window(it.next());// 切换到新窗口

Logger.Output(LogType.LogTypeName.INFO, "new page title is "+ window.getTitle());

} catch (Exception e) {

Logger.Output(LogType.LogTypeName.ERROR,"法切换到新打开窗口"+ e.getMessage());  

}

//driver.close();//关闭当前焦点所在的窗口

}

// driver.switchTo().window(currentWindow);//回到原来页面

// 拖拽滚动条到某一个元素

public void scorllToElement( WebElement ele) {

JavascriptExecutor je = (JavascriptExecutor) driver;   

je.executeScript("arguments[0].scrollIntoView(true);", ele); 

}

}前面几个关于鼠标点击和文本框输入方法,你应该看得懂,多个窗口之间driver的切换,看不懂也没关系,等以后遇到了切换的问题再去读代码。

这个时候,我们把pageobjects下HomePage.java优化下,继承BasePage类。

package pageobjects.homepage;

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import myframework.BasePage;

import pageobjects.dashboard.DashBoardPage;

public class HomePage extends BasePage {

/**

* 页面对象类,主要写页面定位的地址,下面写各种页面actions的方法

*/

public HomePage(WebDriver driver) {

super(driver);

}

// 元素定位

// 登录链接

@FindBy (xpath=".//*[@id="meta-2"]/ul/li[1]/a")

WebElement login_link;

// 用户名输入框

@FindBy (id="user_login")

WebElement userName_inputBox;

// 密码输入框

@FindBy (id="user_pass")

WebElement password_inputBox;

// 登录提交按钮

@FindBy (id="wp-submit")

WebElement loginSubmitBtn;

// actions方法

// 点击登录链接

public void clickLoginLnk() {

login_link.click();

}

// 输入用户名文本框

public HomePage typeUserName(String userName) {

userName_inputBox.sendKeys(userName);

return new HomePage(driver);

}

// 输入密码框输入

public HomePage typeUserPasswd(String password) {

password_inputBox.sendKeys(password);

return new HomePage(driver);

}

// 点击登录提交行为

public HomePage clickLoginSubmitBtn() {

loginSubmitBtn.click();

return new HomePage(driver);

}

// 构造登录方法

public DashBoardPage login(String userName, String password) {

typeUserName(userName);

typeUserPasswd(password);

clickLoginSubmitBtn();

return new DashBoardPage(driver);

}

// 首页下拉滚动条,参考元素是登录链接

public HomePage scrollToLogin() {

scorllToElement(login_link);

return new HomePage(driver);

}

}

这里我把登录后跳转页面定义成DashBoardPage.java,关于用户登录后名称是否在右上角显示判断就写这个页面类。注意HomePage里登录方法中return语句返回的是一个新的DashBoardPage对象。

package pageobjects.dashboard;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import myframework.BasePage;

package pageobjects.dashboard;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import myframework.BasePage;

public class DashBoardPage extends BasePage {

/**

* 登录进来默认跳转到仪表盘页面

*/

public DashBoardPage(WebDriver driver){

super(driver);

}

// 定位元素右上角显示位置

@FindBy (xpath =".//*[@id="wp-admin-bar-my-account"]/a/span")

WebElement userName;

// 判断用户名称是否在右上角显示

public boolean verifyUserNameIsDisplayed(){

return userName.isDisplayed();

}

}

下面开始真正的第一个TestNG用例的编写,这里我也不会详细给你解释什么是TestNG,还是这句话,我在每篇文章出现新的知识点,我会告诉你这个是干嘛用的,相关详细介绍,你自己去搜索。

还是用之前的TestLogin.java类,TestNG需要添加当当前项目的library。

package testsuites.homepage;

import java.io.IOException;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.support.PageFactory;

import org.testng.Assert;

import org.testng.annotations.AfterClass;

import org.testng.annotations.BeforeClass;

import org.testng.annotations.Test;

import myframework.BrowserEngine;

import pageobjects.dashboard.DashBoardPage;

import pageobjects.homepage.HomePage;

public class TestLogin {

public WebDriver driver;

@BeforeClass

public void setUp () throws IOException {

BrowserEngine browser = new BrowserEngine();

browser.initConfigData(); // 先读取配置文件得到浏览器信息,否则不写运行下面代码出现空指针异常

driver = browser.getBrowser(); // 这个很关键,需要认真思考问什么要这样写

}

@AfterClass

public void  tearDown() {

driver.quit();

}

@Test

public void verifyLogin() {

HomePage hp = PageFactory.initElements(driver, HomePage.class);

//

hp.scrollToLogin();

hp.clickLoginLnk();

hp.login("root", "123456");

// 跳转下一个页面,所以需要换新页面的对象来操作

DashBoardPage dbp = PageFactory.initElements(driver, DashBoardPage.class);

// 断言

Assert.assertTrue(dbp.verifyUserNameIsDisplayed());

}

}

我在chrome是运行成功。

@BeforeClass 和@AfterClass,都是字面意思,该测试类运行前和运行后要做的事情就写到这两个方法中,所有测试利用都@Test开头。这里我们还利用了TestNG的Assert这个硬断言方法。这样写测试脚本,发现代码比较少,最多的反而是导入语句包。本实例中,关于homepage上的登录操作,还是可以进一步构造函数,测试代码直接调用login函数就完成登录,包括拖拽滚动条,点击登录,输入账号等,你可以试试修改下,看看能不能成功。