项目结构介绍
在编写自动化测试程序时,就需要操作Web页面上的元素。然而Web页面上的元素的定位路径或定位方式又具有易变性,所以直接式编程的方式是及其脆弱的。
page object 模型简介
page对象的一个基本法则是:凡是人能做的事,page对象通过软件客户端都能做到。因此,它应当提供一个易于编程的接口,并隐藏窗口中的底层部件。当访问一个文本框时,应该通过一个访问方法(Accessor Methos)实现字符串的获取与返回,复选框应当使用布尔值,按钮应当被表示行为导向的方法名。page对象应当把GUI控件上所有查询和操作数据进行封装为方法。page对象一个好的经验法则时,即使改变具体的元素,page对象的接口也不应发生改变。
Page Object 原理
Page Object 是一种设计模式,在自动画测试开发中遵循这种设计模式来编写代码。 Page Object 应遵循一下原则进行开发:
- Page Object 应该以用于使用
- 有清晰的结构,如 PageObject 对应页面对象,PageModules 对应页面的内容
- 只写测试内容,不写基础内容
- 在可能的情况下防止样板代码
- 不需要自己管理浏览器
- 在运行时选择浏览器,而不是类级别。
- 不需要直接接触Selenium。
介绍目录结构
Page Object 项目结构图
目录/文件说明
- modeling/:GUI控件上所有查询和操作数据进行封装为方法。
- page_object/:用于存放page层的封装。
- test_dir/:测试用例目录。
- test_iperation/:测试用例操作方法封装。
- test_report/:测试报告目录。
- conftest.ts:测试配置文件。
- run_tests.ts:测试运行文件。
命名与设计规范
- 对于page层的封装存放于page_object/目录,命名规范为“xxx_page.ts”.
- 对于测试用例的编写存放于test_dir/目录,命名规范为“test_xxx.ts”
- 每一个功能对应一个测试类,并以“Test” 开头, 如“TestLogin”、“TestSearch”等。
- 在一个测试类下编写功能点的所有测试用例,如“testLoginUserNull”、”testLoginPawdNull“、”testLoginSuccess“等。
文件代码
modeling 目录
./src/modeling/loging.ts
export function debug(msg: string) {
console.log(msg)
}
export function info(msg: string) {
console.log(msg)
}
export function error(msg: string) {
console.log(msg)
}
export function warn(msg: string) {
console.log(msg)
}
export function fatal(msg: string) {
console.log(msg)
}
export function print(msg: string) {
console.log(msg)
}
./src/modeling/page_objects.ts
import { Builder, By, Key, Locator, logging, ThenableWebDriver, until, WebDriver, WebElement, WebElementPromise} from "selenium-webdriver";
import * as logger from "./logging";
export abstract class PageObject {
private url: string;
protected setUrl(url: string) {
this.url = url;
}
public async get(uri: string): Promise<void> {
await this.driver.get(this.url)
}
public constructor(protected driver: ThenableWebDriver|WebDriver, url:string="") {
this.url = url;
this.driver = driver;
}
}
export class PageElement {
driver: ThenableWebDriver|WebDriver;
locator: Locator;
timeOut: number;
describe: string;
index: number;
constructor(driver:ThenableWebDriver|WebDriver, locator: Locator, timeOut: number=5000, describe="undefined", index=0) {
this.driver = driver;
this.locator = locator;
this.timeOut = timeOut;
this.describe = describe;
this.index = index;
}
public async __findElement(driver=this.driver, locator:Locator) {
try {
await driver.wait(until.elementLocated(locator), this.timeOut);
let errorMsg: string = `? Find element:: ${locator}`;
logger.info(errorMsg);
} catch (error) {
let errorMsg: string = `? Find 0 elements through: ${locator}, describe:${this.describe}`;
let end_date: Date = new Date();
console.log("end_date = " + end_date);
logger.error(errorMsg)
throw Error(errorMsg);
}
return await driver.findElement(locator)
}
public async getElement(driver=this.driver, locator:Locator): Promise<WebElement> {
let elem = await this.__findElement(driver, locator);
if(false) {
}
return elem;
}
public async clear(){
let elem = await this.getElement(this.driver, this.locator);
logger.info(`clear element: ${this.describe}`);
elem.clear()
}
public async sendKeys(value: string) {
let elem = await this.getElement(this.driver, this.locator);
logger.info(`🖋 input element: ${this.describe}`);
(await elem).sendKeys(value)
}
public async click() {
let elem = await this.getElement(this.driver, this.locator);
logger.info(`🖱 click element: ${this.describe}`);
(await elem).click()
}
public async submit() {
let elem = await this.getElement(this.driver, this.locator);
logger.info(`submit element: ${this.describe}`);
(await elem).submit()
}
public async getTagName() {
let elem = await this.getElement(this.driver, this.locator);
(await elem).getTagName()
}
public async getText(){
let elem = await this.getElement(this.driver, this.locator);
return elem.getText()
}
public async getSize(){
let elem = await this.getElement(this.driver, this.locator);
return elem.getSize()
}
public async getCssValue(property_name: string){
let elem = await this.getElement(this.driver, this.locator);
return elem.getCssValue(property_name)
}
public async getAttribute(name: string){
let elem = await this.getElement(this.driver, this.locator);
return elem.getAttribute(name)
}
public async isDisplayed(){
let elem = await this.getElement(this.driver, this.locator);
return elem.isDisplayed()
}
public async findElement(locator: Locator){
let elem = await this.getElement(this.driver, this.locator);
return elem.findElement(locator)
}
public async findElements(locator: Locator){
let elem = await this.getElement(this.driver, this.locator);
return elem.findElements(locator)
}
public async getId(){
let elem = await this.getElement(this.driver, this.locator);
return elem.getId()
}
public async getLocation(){
let elem = await this.getElement(this.driver, this.locator);
return elem.getLocation()
}
public async getRect(){
let elem = await this.getElement(this.driver, this.locator);
return elem.getRect()
}
public async isEnabled(){
let elem = await this.getElement(this.driver, this.locator);
return elem.isEnabled()
}
public async isSelected(){
let elem = await this.getElement(this.driver, this.locator);
return elem.isSelected()
}
public async selectByValue(value: string){
let elem = await this.getElement(this.driver, this.locator);
await elem.click();
await elem.findElement(By.css(`option[value="${value}"]`)).click;
}
public async selectByIndex(index: number){
let elem = await this.getElement(this.driver, this.locator);
await elem.click();
await elem.findElement(By.css(`option:nth-child(${index})`)).click;
}
public async selectByText(text: string){
let elem = await this.getElement(this.driver, this.locator);
await elem.click();
await elem.findElement(By.css(`option:textContent="${text}")`)).click;
}
public async takeScreenshot(){
let elem = await this.getElement(this.driver, this.locator);
return elem.takeScreenshot()
}
public async enter(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.END)
}
public async SelectAll(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.CONTROL, "a")
}
public async cut(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.CONTROL, "x")
}
public async copy(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.CONTROL, "c")
}
public async paste(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.CONTROL, "v")
}
public async backspace(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.BACK_SPACE)
}
public async delete(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.DELETE)
}
public async tab(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.TAB)
}
public async space(){
let elem = await this.getElement(this.driver, this.locator);
return elem.sendKeys(Key.SPACE)
}
}
export class PageElements {
driver: ThenableWebDriver|WebDriver;
locator: Locator;
timeOut: number;
describe: string;
index: number;
constructor(driver:ThenableWebDriver|WebDriver, locator: Locator, timeOut: number=5000, describe="undefined", index=0) {
this.driver = driver;
this.locator = locator;
this.timeOut = timeOut;
this.describe = describe;
this.index = index;
}
public async find(){
let elems: WebElement[] = []
try {
elems = await this.driver.findElements( this.locator);
} catch (error) {
elems = [];
}
logger.info(`? Find ${elems.length} elements through: ${this.locator}, describe:${this.describe}`);
return elems;
}
}
page_object 目录
./src/baidu_oage.ts
import { By, ThenableWebDriver, WebDriver } from "selenium-webdriver";
import { PageElement, PageObject } from "../modeling/page_objects";
export class BaiduPage extends PageObject{
driver: ThenableWebDriver|WebDriver
constructor(driver: ThenableWebDriver|WebDriver) {
super(driver);
this.driver = driver
}
inp = new PageElement(this.driver, By.css('#kw'));
btn = new PageElement(this.driver, By.css('#su'), 3000, "定位百度一下按钮");
}
./src/draw_page.ts
import { By, ThenableWebDriver, WebDriver } from "selenium-webdriver";
import { PageElement, PageObject } from "../modeling/page_objects";
export class DrawPage extends PageObject{
driver: ThenableWebDriver|WebDriver
constructor(driver: ThenableWebDriver|WebDriver) {
super(driver);
this.driver = driver
}
colorList = new PageElement(this.driver, By.id('color-list'));
}
test_dir
./src/test_dir/singeinstance.ts
import { WebDriver, Builder } from "selenium-webdriver";
export class SingleInstance{
private static instance: WebDriver;
public static create_driver() {
if(! this.instance) {
this.instance = new Builder().forBrowser("chrome").build()
}
return this.instance
}
}
./src/test_dir/excutejs.ts
import { Builder, By, Locator, logging, ThenableWebDriver, until, WebDriver, WebElement, WebElementPromise} from "selenium-webdriver";
import { BaiduPage } from "../page_object/baidu_page";
import { SingleInstance } from "./singleinstance";
var sleep = function(time: number) {
var timeOut = new Date().getTime() + time;
while(new Date().getTime() <= timeOut) {};
};
async function main() {
const driver = SingleInstance.create_driver();
await driver.get("https://www.baidu.com");
const page = new BaiduPage(driver);
await page.inp.sendKeys("helloworld")
await page.inp.clear()
await page.inp.sendKeys("成功")
let satrar_date: Date = new Date();
console.log("satrar_date = " + satrar_date);
await page.btn.click()
await driver.wait(until.elementLocated(By.xpath('//a[@class="toindex"]')), 10000).click;
const res = await driver.findElement(By.xpath('//a[@class="toindex"]')).getText()
console.log("--------------------------------------------")
console.log(By.xpath('//a[@class="toindex"]').using)
console.log(By.xpath('//a[@class="toindex"]').value)
console.log(res)
console.log("--------------------------------------------")
}
main()
test_operation 和 test_report目录目前为空
|