前段时间用的 UI 自动化测试框架主要是两个:Macaca 和 Appium。后来基于了非常多的原因,还是用了大家都用的 Appium(这家伙对 iOS10 支持非常不好)。如果你刚接触这两个包装着 Mocha 的测试框架,而且想用 Node 去写整个测试框架,都会发现它们共同的毛病:慢,卡,不稳定,代码不方便阅读等等。这些是很多原因造成的,比如之前用 Macaca 写登录脚本是一长串 Promise。

测试用例写出来后,在模拟器上跑着还不稳定,上一次用例通过了,这一次用例失败了,加上测试速度,格外让人蛋疼。后来经过大神指点迷津,在书写方式和架构上面进行了修改,整个项目看起来清爽不少。

一番改造后,抛弃了 Promise,使用 Generator 和 yield,最后的要点就是人们常说的 “高耦合,低内聚”。

yiewd

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 类提供的一条龙服务。

改造下来,代码看着舒服多了,而且没有用例这次通过,下次挂的情况。


↙↙↙阅读原文可查看相关链接,并与作者交流