基于CodeceptJS,从0到1搭建E2E自动化测试框架

CodeceptJS

CodeceptJS是基于现代web技术的E2E自动化测试框<wbr>架,具有特殊的BDD风格语法;基于 Feature 和 Scenario 两个粒度来组织测试让它看起来更有 E2E 测试的样子,它支持最新的ES6语法,<wbr>同时也屏蔽各种复杂的回调细节,<wbr>所有的测试用例都是以第一人称来做,<wbr>让测试代码阅读起来更像是自然语言。

Quick Start

第一步,先让测试跑起来

按照quick start搭建了环境,写出了第一个测试-登陆,感觉so easy.

Feature('My First Test');

Scenario('test something', ({ I }) => {
  I.amOnPage('https://github.com');
  I.click('Sign in');
  I.fillField('username', 'myName');
  I.fillField('password', 'myPassword');
  I.click('Sign in');
  I.see('Welcome, myName');
});

CI

第一个测试成功了,赶紧放到CI上跑一跑,每天定时运行,<wbr>自动做回归测试,想想都觉得很美好。 那么问题来了,CI server上没有安装测试工具所需的环境依赖怎么办?<wbr>在哪个环境运行,DEV,QA,UAT?<wbr>运行完以后有report吗?

别急,都有现成的解决方案。
在CI server上启动一个包含测试环境依赖的docker,<wbr>先让测试运行在最可控的QA环境,<wbr>搜索一款最适合当前工具的report,比如allure,<wbr>跟devops小哥哥pair一下,<wbr>jenkinsfile就生成了,<wbr>E2E自动化测试每天定时跑起来了,<wbr>测试运行失败就会发送消息到日常工作群,及时发现问题~

pipeline {
  agent {
    kubernetes {
      label 'swgen2-e2e'
      defaultContainer 'jnlp'
      yaml """
        apiVersion: v1
        kind: Pod
        metadata:
        labels:
          app: swgen2-e2e-app
        spec:
          containers:
          - name: codecepjs
            image: codeception/codeceptjs
            command:
            - cat
            tty: true
      """
    }
  }

    stages {
        stage('Run e2e in qa') {
            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    container('codecepjs') {
                    sh 'codeceptjs run --grep @qa -o \'{ "helpers": {"TestCafe": {"url": "https://swgen2-qa.successwareg2.com"}}}\' --plugins allure'
                    }
                  }
                }
        }
    }

    post {
        always {
            script {
                allure([
                        includeProperties: false,
                        jdk: '',
                        properties: [],
                        reportBuildPolicy: 'ALWAYS',
                        results: [[path: 'output']]
                ])
            }
        }
        failure {
            slackSend(color: '#FF0000',
                    channel: '#swplatform-jenkins',
                    message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL}) \nCommitter: (${getCommitter()})")
        }
        fixed {
            slackSend(color: '#00FF00',
                    channel: '#swplatform-jenkins',
                    message: "BACK TO NORMAL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
    }
}

新的问题

CodeceptJS支持语义化的元素定位符,<wbr>可以自动判断定位符类型并且寻找元素,<wbr>对于编写测试代码来说确实很方便,不用辛辛苦苦找元素定位了,<wbr>可是有些元素通过文本作为定位符找不到啊,<wbr>而且通过文本找元素好像很慢?还有哪些元素定位的方式?
CodeceptJS的操作就支持amOnPage,<wbr>click,fillField吗?我还有一些其他的操作,<wbr>应该怎么写啊?
CodeceptJS的断言只支持see吗?<wbr>我还有地方要用not see,它也能支持吗?

这几个问题抛出了学习一个自动化测试工具的基本路径,<wbr>那就是学习元素定位符,操作,断言,<wbr>其中元素定位符对于一般的UI自动化测试工具都是通用的,<wbr>而操作和断言,每个工具提供的语法不一样。然而,<wbr>一旦掌握一种工具,其他任何工具学习起来都很快。学好数理化,<wbr>走遍天下都不怕~

持续优化

此刻,我的元素定位符,操作,断言已经写得很溜了。 当我开始写越来越多的测试后发现,每个测试都要登陆一下,<wbr>怎么样才能减少这种重复操作呢? 除了登录,我的代码中很多重复操作,怎么样封装他们,<wbr>方便调用呢? dev小伙伴升级了依赖库,导致元素定位变化了,<wbr>我需要大面积修改元素定位符,怎样才能提高元素定位符的通用性,<wbr>避免此类情况再次发生?

第一步,提取公共元素,比如文本框,<wbr>当我需要使用某个文本框时只需要输入文本框的label就能定位<wbr>到它了;

//封装公共元素
editInput: function (inputName, text) {
      let locator = `//label[contains(., "${inputName}")]/preceding-sibling::input`;
      this.clearField(locator);
      this.appendField(locator, text);
    },

//

第二步,提取公共step,比如click tab作为common step放到一个文件里,当我在任何页面需要click tab时,只需要写上tab上的文字就可以了,<wbr>codeceptjs对button和link做了一层封装,<wbr>所以看到quickstart里面,click sign in的元素定位符只需要写上文字就可以了;

第三步,提取公共function,比如登录,<wbr>搜索等不同页面常用操作放到common function文件里面;

第四步,提取每个页面的公共function,<wbr>针对一个页面的某个feature如果有多种测试场景,<wbr>此时公共function可以复用。

场景设计的巧思

测试场景设计需要许多巧思,<wbr>这样才能让E2E在日常运行中更为流畅。

  • 尽可能精简。因为E2E保证的是主要功能和流程正确,<wbr>掺杂太多细节验证会让代码复杂度急剧增加,维护成本太高,<wbr>同时还会让自己陷入我到底应该把哪些测试点加入到E2E的焦虑中<wbr>。先用20%的精力做成80%的事情,再用剩下的80%<wbr>的精力想想怎么拔高20%,比如提高团队的质量意识。
  • 每个测试场景尽可能独立。这样在其中一个场景报错时,<wbr>不会影响其他场景的测试结果,<wbr>并且任何时候想单独运行某个或某几个测试都可以立即运行,<wbr>不用修改代码才能运行。场景独立带来的副作用就是,<wbr>会有许多重复操作,这时就需要封装来解决这些冗余代码。
  • 在测试套件中进行登录操作。测试场景独立的话,<wbr>每个测试开始前都需要进行登录操作,这会拉慢测试运行速度,<wbr>如果测试工具支持的话,<wbr>设置为在每个测试套件开始前进行登录操作,<wbr>这会大大减少重复的登录操作。
  • 需要考虑数据的初始化。<wbr>数据初始化的方案要么是通过页面操作去造数据,<wbr>它的缺点是添加了大量和测试场景无关的额外操作,<wbr>使得代码复杂加运行时间变长;要么通过API造数据,<wbr>但需要E2E测试工具支持;要么通过连接数据库,<wbr>直接向数据库插入数据,缺点是数据库表之间关联关系复杂的话,<wbr>难以摸清这些关联关系,容易造出脏数据,<wbr>同时连接数据库也需要E2E测试工具支持;<wbr>我最倾向的方式是在不受影响的环境中运行E2E,<wbr>使用提前准备好的固定数据,当然用完就失效的数据,<wbr>比如跟状态相关的数据,还是只能通过前三种方式创建。
  • 需要考虑数据使用后的还原。这样才能在下次运行同一个测试时,<wbr>不会因为数据被改变而失败。
  • 加tag区分不同环境的测试。<wbr>针对不同环境运行的测试场景不一样的情况,<wbr>加tag区分不同环境,运行时加上tag参数即可。

更多思考

E2E总是被诟病,运行太慢,维护频繁,反馈太晚。<wbr>但在实际使用过程中,E2E发现了不少新功能破坏旧功能的情况,<wbr>然而单元测试没有发现这些问题(UT覆盖率也很高)。<wbr>那么为了更好地使用E2E,就需要不断优化它。运行太慢,<wbr>那么就在多个docker里面并发运行;维护频繁,<wbr>那么就尽量用不容易变化的元素定位符,并且多封装,<wbr>一旦发生变化改动也不会很多;反馈太晚,额。。。<wbr>它的属性就是如此,<wbr>用它来做日常的回归测试保证旧功能完好是个不错的选择。

最后的成果

目前我们的项目上,一共有31个测试场景,<wbr>单个docker容器里面运行全部测试耗时大概13分钟,<wbr>每次部署QA或者UAT后自动trigger E2E分别在不同环境运行,<wbr>再也不用担心Dev的重构或者环境部署不当造成的bug没有及时<wbr>发现了。另外,因为有很多公共元素和公共步骤的封装,<wbr>添加新的测试代码也很容易。唯一令人头大的点就是,<wbr>它有时候有点不稳定,报一些手动操作没有的bug,这类bug debug起来很难,也不知道算不算bug。。。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容