团队最近采用了一种与之前的PageFactory思路完全不一样的模式来完成我们的自动化测试编码
在编写step过程中,有很多非常通用的步骤定义,这些步骤定义可编写大量与之类似的场景,而无需创建太多的步骤定义。
即采用一种命令式风格来编写场景步骤,使用fill,press这样的词语,对于这样一种风格有很多争议,但是不管好坏,尝试下总归是有好处的。
这种命令式的风格,是属于用户界面控件的直接操作,所以我们会编写很多通用的step,而使用者只需要传入控件元素属性和其他对于值即可。
如 I fill in "Input_name" with "test"
意思是:在输入框“Input_name”中输入“test”
其中Input_name是对应元素的别称,我们把所有页面的元素都统一组织在一个文件里面去
下面就开始搭建该框架,以登录csdn为例子
在项目根目录下创建一个目录dataEngine,在该目录下创建一个文件ObjectRepository.properties,该文件用于保存CSDN各个页面上的元素对象
#该文件用于保存元素对象,每个Key是对应元素别称,Value是元素查找方式和查找值拼接,中间用英文符号":"分隔
#需要注意每个元素使用查找方式的Id,Name,Class,Xpath,CssSelector,LinkText,PartialLinkText,Tag是固定的
# Home Page Objects
Userbar_logout=LinkText:登录
Tag_body=Tag:body
#Login Page Objects
Input_name=Id:username
Input_password=Id:password
Login_button=Class:logging
在项目的test->java下,创建目录com,在目录com中创建目录util
在目录util下创建WebDriverUtil.java
上述对象哭文件中,我们目的是用户在编写测试场景时,输入Input_name,底层代码可以直接定位到元素,则对该文件进行解析
所以,首先操作对象库
/** 操作对象库,根据传进来的Key,来获取Value
* @param a(对应feature中传进来的元素名称即对应的是属性文件中的Key值)
* @return(返回对应的values)
*/
public static String readValue(String a){
Properties defaultProperties = new Properties();
String popath = "dataEngine/ObjectRepository.properties";
String value=null;
try {
defaultProperties.load(new FileReader(popath));
value = defaultProperties.getProperty(a);
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
return value;
}
之后根据获取到的Value,来获取元素的By值
/**
* 根据对象库中的Value来获取By的值
* @param field(对应feature中传进来的元素名称)
* @return
*/
public static By getObjectLocator(String field) {
String locatorProperty = readValue(field);
System.out.println("解析该元素" + locatorProperty.toString());
String locatorType = locatorProperty.split(":")[0];
String locatorValue = locatorProperty.split(":")[1];
By locator = null;
if (locatorType.contains("Id")){
locator = By.id(locatorValue);
}else if (locatorType.contains("Name")){
locator = By.name(locatorValue);
}else if (locatorType.contains("Class")){
locator = By.className(locatorValue);
}else if (locatorType.contains("Xpath")){
locator = By.xpath(locatorValue);
} else if (locatorType.contains("CssSelector")){
locator = By.cssSelector(locatorValue);
}else if (locatorType.contains("LinkText")){
locator = By.linkText(locatorValue);
}else if (locatorType.contains("PartialLinkText")){
locator = By.partialLinkText(locatorValue);
}else if (locatorType.contains("Tag")){
locator = By.tagName(locatorValue);
}
return locator;
}
然后再根据上面获取到的By值来定位到可见元素
则WebDriverUtil.java文件的完整代码:import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import java.io.*;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class WebDriverUtil {
/**
* 该方法用于查找可见的元素
* @param webDriver
* @param field(对应feature中传进来的元素名称)
* @return
*/
public static WebElement findFieldWithToBeVisibility(WebDriver webDriver, String field) {
try {
Wait wait = new FluentWait(webDriver)
.withTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class);
final By locate = getObjectLocator(field);
wait.until(ExpectedConditions.presenceOfElementLocated(locate));
wait.until(ExpectedConditions.visibilityOfElementLocated(locate));
return webDriver.findElement(locate);
} catch(NoSuchElementException exception) {
throw exception;
}
}
/**
* 根据对象库中的Value来获取By的值
* @param field(对应feature中传进来的元素名称)
* @return
*/
public static By getObjectLocator(String field) {
String locatorProperty = readValue(field);
System.out.println("解析元素 " + field + "=" + locatorProperty.toString());
String locatorType = locatorProperty.split(":")[0];
String locatorValue = locatorProperty.split(":")[1];
By locator = null;
if (locatorType.contains("Id")){
locator = By.id(locatorValue);
}else if (locatorType.contains("Name")){
locator = By.name(locatorValue);
}else if (locatorType.contains("Class")){
locator = By.className(locatorValue);
}else if (locatorType.contains("Xpath")){
locator = By.xpath(locatorValue);
} else if (locatorType.contains("CssSelector")){
locator = By.cssSelector(locatorValue);
}else if (locatorType.contains("LinkText")){
locator = By.linkText(locatorValue);
}else if (locatorType.contains("PartialLinkText")){
locator = By.partialLinkText(locatorValue);
}else if (locatorType.contains("Tag")){
locator = By.tagName(locatorValue);
}
return locator;
}
/** 操作对象库,根据传进来的Key,来获取Value
* @param a(对应feature中传进来的元素名称即对应的是属性文件中的Key值)
* @return(返回对应的values)
*/
public static String readValue(String a){
Properties defaultProperties = new Properties();
String popath = "dataEngine/ObjectRepository.properties";
String value=null;
try {
defaultProperties.load(new FileReader(popath));
value = defaultProperties.getProperty(a);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
}
则通过该文件,我们就解析了对象库,并获取到对应的元素位置
然后在该目录下创建一个文件login.feature,像编写测试用例步骤一样编写我们预期的step:
Feature: Login
@login
Scenario Outline: login in csdn
#打开csdn首页
Given I am on csdn
#单击登录按钮
And I press "Userbar_login"
#输入用户名
And I fill in "Input_name" with ""
#输入密码
And I fill in "Input_password" with ""
And I press "Login_button"
#登录成功后,可查到到用户名"echo_茵子",可以修改成自己的CSDN用户名
Then should see text "echo_茵子" in "Tag_body"
Examples:
|username |password |
#这里输入对应的用户名和密码
|***** |***** |
import com.config.ConfigManager;
import com.util.SharedDriver;
import cucumber.api.java.en.Given;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import java.util.concurrent.TimeUnit;
public class IAmOnStepDefs {
private final WebDriver driver;
private final ConfigManager config;
private static Wait wait ;
private static String baseUrl;
public IAmOnStepDefs(SharedDriver driver, ConfigManager config) {
this.driver = driver;
this.config = config;
wait = new FluentWait(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class);
}
/**
*该方法用于实现csdn页面的跳转
*/
@Given("^I am on csdn$")
public void i_am_on() throws Throwable {
baseUrl = this.config.get("base_path");
this.driver.navigate().to(baseUrl);
wait.until((new ExpectedCondition() {
@Override
public Boolean apply(WebDriver driverObject) {
System.out.println("*****loading page*****");
return (Boolean) ((JavascriptExecutor) driverObject).executeScript("return document.readyState === 'complete'");
}
}));
}
}
可看到第一个step已经可以正常运行了,后面五个步骤还没定义
import com.util.SharedDriver;
import com.util.WebDriverUtil;
import cucumber.api.java.en.When;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
public class IPressStepDefs {
private final WebDriver driver;
public IPressStepDefs(SharedDriver driver) {
this.driver = driver;
}
/**
* 通过 field 找到某个元素,并进行点击
*/
@When("^I press \"([^\"]*)\"$")
public void i_press(String field) throws Throwable {
WebElement element = WebDriverUtil.findFieldWithToBeVisibility(this.driver,field);
new Actions(driver).moveToElement(element).perform();
element.click();
}
}
创建IFillInWithStepDefs.java,内容如下所示
import com.util.SharedDriver;
import com.util.WebDriverUtil;
import cucumber.api.java.en.When;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
public class IFillInWithStepDefs {
private final WebDriver driver;
public IFillInWithStepDefs(SharedDriver driver) {
this.driver = driver;
}
/**
* 通过 field 找到某个元素,并向该元素输入文字
*/
@When("^I fill in \"([^\"]*)\" with \"([^\"]*)\"$")
public void i_fill_in_with(String field, String content) throws Throwable {
WebElement element = WebDriverUtil.findFieldWithToBeVisibility(this.driver,field);
if (null != element) {
element.clear();
element.sendKeys(content);
}
}
}
创建ShouldSeeTextForElement.java,内容如下所示
import com.util.SharedDriver;
import com.util.WebDriverUtil;
import cucumber.api.java.en.Then;
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import java.util.concurrent.TimeUnit;
public class ShouldSeeTextForElement {
private final WebDriver driver;
private final Wait wait;
public ShouldSeeTextForElement(SharedDriver driver) {
this.driver = driver;
wait = new FluentWait(this.driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class);
}
/**
* 找到某个元素,并验证该元素存在内容text
*/
@Then("^should see text \"([^\"]*)\" in \"([^\"]*)\"$")
public void should_see_text(String text,String field) throws Throwable {
try{
WebElement element = WebDriverUtil.findFieldWithToBeVisibility(driver,field);
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
wait.until(ExpectedConditions.textToBePresentInElement(element,text));
}catch(Exception e){
e.printStackTrace();
}
}
}
则目前整个项目的目录结构如下所示:
这里是因为我们在对象库中使用了中文,默认情况下中文会被读成乱码,所以我们需要设置改下之前读取配置文件的代码:
/** 操作对象库,根据传进来的Key,来获取Value
* @param a(对应feature中传进来的元素名称即对应的是属性文件中的Key值)
* @return(返回对应的values)
*/
public static String readValue(String a){
Properties defaultProperties = new Properties();
String popath = "dataEngine/ObjectRepository.properties";
String value=null;
try {
InputStream in=new FileInputStream(popath);
InputStreamReader re=new InputStreamReader(in,"GB2312");
BufferedReader bf=new BufferedReader(re);
defaultProperties.load(bf);
value = defaultProperties.getProperty(a);
System.out.println(value);
in.close(); // 关闭流
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
之后使用MAVEN中再次运行程序,查看日志
则上述我们就搭建了一个采用命令行风格来编写测试场景的小框架,欢迎大家批评指正~
源代码在该链接,可免费下载:http://download.csdn.net/detail/yan1234abcd/9630675