1.需求分析
随着产品规模不断完善,功能模块逐渐增加,传统的人工测试逐步变得低效。为了提高测试效率,可以将简单的功能测试点交由自动化执行,让测试人员可以空出更多的时间去关注业务,流程。而传统的自动化测试脚本由于维护性差,成本高,收益低等因素导致难以有效的推行。而测试平台化规划好的话能将业务与代码的耦合度降低,有利于后期维护与可持续集成。
2.方案设想
所有数据均保存在数据库中,提供Web页面进行操作,其中
2.自定义方法:平台提供浏览器基础操作并提供功能用以保存测试人员自定义的方法,例如:
平台提供基础的点击元素以及向元素输入信息基础功能。结合页面数据保存登录页的自定义方法向用户名输入框中输入用户名,向密码输入框中输入密码,向验证码输入框中输入验证码,点击登录按钮等。后续通过关键字驱动进行调用。
3.自定义类方法:自定义方法并不能解决所有的需求,有些复杂的逻辑时,上述的方式并不适用,所以需要通过类中实现逻辑方法并提供给外界调用。例如需求:在一个table中需要通过判断某元素的内容,符合预期后再进行操作
以上三种结合起来生成测试用例。并与测试数据形成测试用例集(关键字驱动以及数据驱动相结合的混合驱动模式)再通过所选用配置最终形成自动化执行的测试脚本。(配置:解决测试用例运行环境,浏览器等相关问题)然后交给执行引擎执行并且输出测试报告
3.核心代码
1.基础父类
/**
* 基础父类,所有PageObject类均继承该类
*
* @author miduo
*
*/
public class Page {
private WebDriver driver;
public Page(WebDriver driver) {
super();
this.driver = driver;
if (this.driver != null) {
this.driver.manage().window().maximize();
}
}
public WebDriver getDriver() {
return driver;
}
@Auto(key = "click")
public void click(String locator) {
findElement(locator).click();
}
@Auto(key = "input")
public void input(String locator, String value) {
findElement(locator).sendKeys(value);
}
@Auto(key = "open")
public void open(String url) {
driver.get(url);
}
/**
* 通过反射获取类自定义方法
*
* @param key
* @return
*/
protected Method getCustomMethodByKey(String key) {
Method[] methods = getClass().getMethods();
for (Method method : methods) {
Auto autoKey = method.getAnnotation(Auto.class);
if (autoKey != null && autoKey.key().equals(key)) {
return method;
}
}
return null;
}
protected final WebElement findElement(String locator) {
int index = locator.indexOf(".");
String type = locator.substring(0, index).toLowerCase();
String value = locator.substring(index + 1);
switch (type) {
case "id":
return findElement(By.id(value));
case "name":
return findElement(By.name(value));
case "css":
return findElement(By.cssSelector(value));
case "xpath":
return findElement(By.xpath(value));
case "class":
return findElement(By.className(value));
case "tag":
return findElement(By.tagName(value));
case "link":
return findElement(By.partialLinkText(value));
default:
return null;
}
}
private final WebElement findElement(By by) {
return new WebDriverWait(driver, 10).until(new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver d) {
return d.findElement(by);
}
});
}
}
2.PageObject具体实现类
public class LoginPage extends Page {
public LoginPage(WebDriver driver) {
super(driver);
}
/**
* PageObject自定义类方法:登录
*
* @param userName
* @param password
* @param captcha
*/
@Auto(key = "login")
public void login(String userName, String password, String captcha) {
input("id.Account", userName);
input("id.Password", password);
input("id.Captcha", captcha);
click("xpath.//input[@type='submit']");
}
}
3.自定义方法
public class Step {
private Class<? extends Page> clazz;
private String methodKey;
private Object value;
public Step() {
super();
}
public Step(Class<? extends Page> clazz, String methodKey, Object value) {
super();
this.clazz = clazz == null ? Page.class : clazz;
this.methodKey = methodKey;
this.value = value;
}
public Class<? extends Page> getClazz() {
return clazz;
}
public void setClazz(Class<? extends Page> clazz) {
this.clazz = clazz;
}
public String getMethodKey() {
return methodKey;
}
public void setMethodKey(String methodKey) {
this.methodKey = methodKey;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
4.测试用例以及测试数据
public class TestCase {
private ArrayList<Step> steps;
public TestCase() {
super();
this.steps = new ArrayList<>();
}
public ArrayList<Step> getSteps() {
return steps;
}
public void setSteps(ArrayList<Step> steps) {
this.steps = steps;
}
public void addStep(Step step) {
this.steps.add(step);
}
public Result perform(WebDriver driver, Parameter parameter) {
Result result = new Result();
for (Step step : steps) {
Object[] value = step.getValue();
Object[] coValue = new Object[value.length];
Boolean change= false;
if (parameter != null) {
for (int i = 0; i < value.length; i++) {
String key = value[i].toString();
coValue[i] = key;
if (key.startsWith("${") && key.endsWith("}")) {
change= true;
Object v = parameter.get(key.substring(2, key.length() - 1));
value[i] = v == null ? value[i] : v;
}
}
}
if (!step.perform(driver, value)) {
result.setResult(false);
return result;
}
if (change) {
step.setValue(coValue);
}
}
result.setResult(true);
return result;
}
}
public class Parameter {
private Map<String, Object> parameter;
public Parameter() {
super();
this.parameter = new HashMap<>();
}
public void add(String key, Object value) {
this.parameter.put(key, value);
}
public void remove(String key) {
this.parameter.remove(key);
}
public Object get(String key) {
return this.parameter.get(key);
}
}
5.执行引擎
public class Runner {
public static List<Result> perform(TestCase testCase, List<Parameter> testData) {
List<Result> list = new ArrayList<>();
System.out.println(testData.size());
if (testData != null && testData.size() > 0) {
for (Parameter parameter : testData) {
// 实例化何种浏览器与打开什么环境由配置决定,在此不实现
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "//chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://*********保密********");
list.add(testCase.perform(driver, parameter));
}
} else {
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "//chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://*********保密********");
list.add(testCase.perform(driver, null));
}
return list;
}
}
代码仅作为可行性分析使用,并非最终版本。
测试代码
public class Test {
private static List<Parameter> parameters;
static {
Parameter parameter = new Parameter();
parameter.add("userName", "userName_01");
parameter.add("password", "password_01");
parameter.add("captcha", "captcha_01");
Parameter paramete1r = new Parameter();
paramete1r.add("userName", "userName_02");
paramete1r.add("password", "password_02");
paramete1r.add("captcha", "captcha_02");
parameters = new ArrayList<>();
parameters.add(parameter);
parameters.add(paramete1r);
}
//通过用户自定义保存方法运行
@org.testng.annotations.Test
public void test1() {
TestCase tc = new TestCase();
tc.addStep(new Step(null, "input", "id.Account", "${userName}"));
tc.addStep(new Step(null, "input", "id.Password", "${password}"));
tc.addStep(new Step(null, "input", "id.Captcha", "${captcha}"));
tc.addStep(new Step(null, "click", "xpath.//input[@type='submit']"));
Runner.perform(tc, parameters);
}
//通过自定义类方法运行
@org.testng.annotations.Test
public void test2() {
TestCase tc = new TestCase();
tc.addStep(new Step(LoginPage.class, "login", "${userName}", "${password}", "${captcha}"));
Runner.perform(tc, parameters);
}
}
代码执行效果再通过一个配置文件或者数据库存储PageObject类的路径,即可通过纯数据进行驱动测试运行,后续再持续完善用户,任务,结果等相关模块即可打造较为完善的自动化测试平台,如此一来,测试人员仅需在平台中编辑测试用例与测试数据并新建相关测试任务即可。测试用例以及测试数据的格式为:
Page | Method | value |
---|---|---|
后台登录页 | 输入用户名 | ${userName} |
后台登录页 | 输入密码 | ${password} |
后台登录页 | 输入验证码 | ${captcha} |
后台登录页 | 点击登陆 | - |
userName | password | captcha |
---|---|---|
userName1 | password1 | captcha1 |
userName2 | password2 | captcha2 |
userName3 | password3 | captcha3 |