前段时间用的 UI 自动化测试框架主要是两个:Macaca 和 Appium。后来基于了非常多的原因,还是用了大家都用的 Appium(这家伙对 iOS10 支持非常不好)。如果你刚接触这两个包装着 Mocha 的测试框架,而且想用 Node 去写整个测试框架,都会发现它们共同的毛病:慢,卡,不稳定,代码不方便阅读等等。这些是很多原因造成的,比如之前用 Macaca 写登录脚本是一长串 Promise。
测试用例写出来后,在模拟器上跑着还不稳定,上一次用例通过了,这一次用例失败了,加上测试速度,格外让人蛋疼。后来经过大神指点迷津,在书写方式和架构上面进行了修改,整个项目看起来清爽不少。
一番改造后,抛弃了 Promise,使用 Generator 和 yield,最后的要点就是人们常说的 “高耦合,低内聚”。
Appium 下编写测试用例遵循 WebDriver 协议,Node 下可以用 wd,而在 yiewd 在 wd 外面包了一层,这样你就可以编写更加直观的代码了,解决异步编程问题不是用 Promise,而是用 Generator。
项目结构我改了很多遍,效果都不理想:每个测试用例间如果有相互依赖,一个挂了,下面的接着挂了。然后参考这篇文章,对项目进行了改造:
|----app·····················放置各个版本 app
|----src
| ---- |----cases············测试用例
| ---- |----screen···········Screen 类
| ---- |----util·················工具类
|----cap.js·················环境配置
|----client.js
|----dev.js
screen:这个里面放置的是 Screen 类,主要的场景都可以写一个 Screen 类,Screen 类上包含这个场景上的 UI 操作,比如登陆、后退、下拉刷新、下滑、绘制手势等。例如一个 APP 开始页面,包含关闭弹框,滑动窗口等功能。
import {swipe} from '../util/yiewdAction';
export default class StartScreen {
constructor(driver, context) {
this.driver = driver;
this.context = context;
}
*closeAlert() {
const driver = this.driver;
const element = yield driver.elementByNameIfExists('closeActivity');
if (element) {
yield element.click();
}
yield driver.sleep(3000);
}
*slideScreen() {
const driver = this.driver;
yield swipe(driver, 300, 300, -300, 0, 500);
yield driver.sleep(500);
yield swipe(driver, 300, 300, -300, 0, 500);
yield driver.sleep(500);
yield swipe(driver, 300, 300, -300, 0, 500);
}
*openApp() {
yield this.driver.waitForElementByClassName('Button', 3000, 100).click();
yield this.driver.sleep(5000);
}
}
cases:有了 Screen 类,我们只需要关心测试用例的编写了,从开启 APP 到进入待测页面,可以用 Screen 类的 UI 操作,不需要重复编写,例如:
import '../util/should';
import InitScreen from '../screen/InitScreen';
import StartScreen from '../screen/StartScreen';
import LoginScreen from '../screen/LoginScreen';
import MainScreen from '../screen/MainScreen';
import HomeIndexScreen from '../screen/HomeIndexScreen';
import HomeProductFixedScreen from '../screen/HomeProductFixedScreen';
import {env, nativeContext} from '../caps';
export default function freeList(driver, done) {
const initScreen = new InitScreen(driver, nativeContext);
const startScreen = new StartScreen(driver, nativeContext);
const loginScreen = new LoginScreen(driver, nativeContext);
const mainScreen = new MainScreen(driver, nativeContext);
return driver.run(function *() {
yield *initScreen.acceptAlert();
yield *initScreen.chooseEnv(env);
yield *startScreen.closeAlert();
yield *startScreen.slideScreen();
yield *startScreen.openApp();
yield *mainScreen.closeActivity();
let contexts = yield driver.contexts();
const homeIndexScreen = new HomeIndexScreen(driver, contexts[contexts.length - 1]);
yield *homeIndexScreen.goLogin();
yield *loginScreen.login();
yield *loginScreen.drawGesture();
yield *mainScreen.goLiCai();
//...
done();
});
}
不需要重复写一些操作,进入到待测页面有 Screen 类提供的一条龙服务。
改造下来,代码看着舒服多了,而且没有用例这次通过,下次挂的情况。