资讯专栏INFORMATION COLUMN

撩测试MM神器cypress使用入门

MAX_zuo / 2018人阅读

摘要:上也有几个临时方案,目前我倾向使用自带的来查看。在打开的测试的浏览器中打开切到按下用户按,输入,选择重新刷新并统计代码执行覆盖率。那么,起来为了高撩质测量试代码,起来。

不很久不很久以前

据说某家公司有两位前端,天天撸bug,为啥嘞?只怪测试MM倾人国,轻语哥哥有bug。✧(๑•̀ㅂ•́)و✧ 可是最近两位有点犯愁 Σ(っ °Д °;)っ。测试MM有几次提了紧急bug,都在旁边鼓励他们改bug了,可是线上bug重现排查比较麻烦,而且改了后还发现没改好,惹得测试MM潸然泪下,好生埋汰。怎么办呢?

前端君666某天发现了E2E测试神器cypress后,暗中偷练神功,改bug越来越6,测试MM每天笑着对他说,666你真6,MM好喜欢呀(๑•́ ₃ •̀๑) 另一位前端君555每天面对堆积如山的bug长吁短叹,测试MM提完新bug后都不理他了≡ ̄﹏ ̄≡

作为一个追求代码永无bug、顺带跟测试MM沟通产品的有理想的前端 (ง •̀_•́)ง,我觉得有必要学习一下怎么使用cypress来进行E2E测试,以此来提高代码质量。那么我们来看看怎么入门cypress测试框架。

cypress三问 - 你是谁

cypress是在mocha式API基础上构建的一套开箱可用的E2E测试框架,对比其他测试框架,它提供一套自己的最佳实践方案,无需其他测试工具库,配置方便简单但功能异常强大,可以使用webpack项目配置,还提供了一个强大的GUI图形工具。入门简单,上手方便,怎么舒服怎么来呀 (。→‿←。)

cypressGUI方式的测试使用真实浏览器,非GUI方式使用chrome-headless,不是用模拟方式进行测试,更真实的展现实际环境中的测试过程和结果。

cypress三问 - 你有啥优势

cypress有几大自带的强大功能:

自带GUI工具,想测啥就点啥,还可以查看整个测试过程,想录屏还可以录屏哟(录屏可以发给测试MM看,保准她说哥哥真厉害哟。 一般人我不告诉他๑乛◡乛๑)

测试的每一步都有snapshot,可以通过GUI工具查看每个过程的页面状态,不是截图而是真是的页面DOM环境哟!

自带数据mock和请求拦截机制,还原线上数据引起的bug别提有多轻松了

和wepbakc配置,实现无论修改测试文件还是被测试代码都可以自动重测

小Tips:可以给测试用例加上only或者skip来避免重测测试文件里的所有用例: it.only("只测试这个哟); it.skip("不要测这个");

cypress三问 - 怎么用 安装

yarn add cypress 或者 npm install cypress

安装完毕后,./node_modules/.bin/cypress install安装cypress环境(包括GUI工具)

配置

package.json: 配置GUI和非GUI(terminal)两种方式来运行cypress

    "scripts": {
        "cypress": "cypress run",
        "cypress-gui": "cypress open",

⚠️ 配置好后 先运行 yarn cypress[-gui] 或者 npm run cypress[-gui](中括号意思是可选)来初始化cypress,__生成默认配置和目录__

cypress.json(与package.json同级目录): cypress提供比较灵活的配置,可以根据自己需要定制行为,以下列一下我对一个项目的配置

{
    "baseUrl": "http://localhost:8080", // 本地开发服务地址(webpack-dev-server)
    "integrationFolder": "src", // 自定义"src"为测试文件根目录,默认是"cypress/integration"
    "testFiles": "**/*.cypress.spec.js", // 自定义测试文件的匹配正则,默认是"**/*.*",即所有文件
    "videoRecording": false, // 关闭录屏功能, 如果开启录屏功能,记得将"cypress/screenshots"目录加入".gitignore",防止不小心将录屏加到git中
    "viewportHeight": 800, // 设置测试环境的页面视图的高度
    "viewportWidth": 1600 // 设置测试环境的页面视图的宽度
}

cypress/plugins/index.js: cypress运行环境配置,可以用来配置webpack等。以下是配置webpack别名范例。默认这里不需要配置。

// 参考官方例子地址 https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/preprocessors__typescript-webpack/cypress/plugins/index.js
const wp = require("@cypress/webpack-preprocessor");
const path = require("path");

function resolve(dir) {
    return path.join(__dirname, "../..", dir);
}

module.exports = on => {
    const options = {
        webpackOptions: {
            resolve: {
                alias: {
                    "@": resolve("src"),
                    cypress: resolve("cypress")
                }
            }
        }
    };
    on("file:preprocessor", wp(options));
};
万事俱备,测测测

简单的一个例子

describe("测试页面包含某元素", () => {
    it("有云 "前端哥哥们真帅,前端妹妹们真漂亮"", () => {
        cy.contains("前端哥哥们真帅,前端妹妹们真漂亮");
    });

    it("要有一个链接", () => {
        cy.get("a").should("have.length", 1);
    });

    it("不存在class含有abc的元素", () => {
        cy.get(".abc").should("have.length", 0);
    });
});

互动的例子

describe("一起动", () => {
    it("获取输入框,输入文字并按enter键", () => {
        const text = "not exist";
        // type api用法: https://docs.cypress.io/api/commands/type.html#Usage
        cy.get("input").type(`${text}{enter}`);
    });

    it("点击按钮", () => {
        cy.get("button").click();
    });
});

网络请求mock例子

Tip1: cy.route的路径匹配是严格的,所以要注意是否需要加通配符。如 cy.route("/api/search", [])不会拦截/api/search?keyword=abc,只会拦截/api/search

Tip2: cy.route的method要注意,默认是GETcy.route("/api/posts")cy.route("POST", "/api/posts") 是不一样的。

describe("要啥给啥", () => {
     beforeEach(() => {
        cy.server(); // 一定要在 cy.route 前调用
        cy
            .fixture("/posts/list.json") // 我们在 cypress/fixtures 内创建mock用的数据
            .as("postsData"); // 给 mock 数据取别名,以后 cy.route 使用
        cy
            .route("/api/posts", "@postsData")
            .as("getPostsRoute"); // 给请求取别名,以供 cy.wait 使用
    })

    it("进入列表页,拦截列表请求接口", () => {
        cy.wait("@getPostsRoute"); // 等待被拦截的接口请求完成

        cy.get(".post").should("have.length", 10); // 要有10条数据被渲染到页面上
    });
})

实际场景例子: 结合上面所有姿势,我们现在测试搜索页面的搜索、操作结果

describe("test search page", () => {
    // 几个 route 路径变量
    const searchRoutePath = "/api/items/activities?query=*";
    const deleteActivityRoutePath = "/api/activities/*/items/batch?num_iids[]=*";
    const undoActivityRoutePath = "/api/activities/*/items/undo";

    function search(keyword) {
        // 将搜索行为和等待搜索返回封装起来
        cy
            .fixture("items/activities.json")
            // 处理mock数据,只返回符合搜索结构的数据
            .then(data => data.filter(item => item.title.indexOf(keyword) !== -1))
            .as("searchResult");
        cy.server();
        cy.route(searchRoutePath, "@searchResult").as("searchRoute");

        const input = cy.get("input");
        input.clear(); // 清空输入框内文本

        input.type(`${keyword}{enter}`);

        cy.wait("@searchRoute");
    }

    before(() => {
        // 进行所有测试前,先访问搜索页
        cy.visit("/activities/search");
    });

    it("should show no data tip when search result is empty", () => {
        const text = "not exist";
        search(text);
        cy.contains(`没有找到关于 ${text} 的结果`);
    });

    it("should remove activity from list when clean successful", () => {
        search("成功");

        cy
            .route("delete", deleteActivityRoutePath, {
                success: 0,
                fail: 0,
                waiting: 0,
            })
            .as("deleteActivityResponse");

        // within是让cy执行的context保持在".activities-search"这个dom节点内
        // 默认cy的执行是以上一个cy命令结果作为context
        // 如 "cy.get("a"); cy.get("span")",cy会在上一个命令找到的"a"标签中查找"span"
        cy.get(".activities-search").within(() => {
            const items = cy.get(".result-item");
            items.should("have.length", 1);
            const applyList = items.get(".apply-list");

            applyList.should("not.be.visible"); // 每个数据项内详细内容区域是隐藏的

            const toggleBtn = items.get(".item-apply-count");
            toggleBtn.click(); // 点击显示详细内容区
            applyList.should("be.visible");
            applyList.children().should("have.length", 1); // 详细内容区内数据只有1条

            const cleanBtn = cy.contains("退出");
            cleanBtn.click(); // 点击详细内容区里的“退出”按钮

            cy.wait("@deleteActivityResponse"); // 等待“退出”请求返回
            cy.get(".apply-list").should("be", null); // 退出成功后,详细内容区数据减1,即空
        });
    });
});
几个必读文档

network-requests : https://docs.cypress.io/guide...

assertions : https://docs.cypress.io/guide...

recipes 示例 : https://docs.cypress.io/examp...

code completion 代码提示: https://docs.cypress.io/guide...

关于测试覆盖率

目前cypress没有内置测试覆盖率统计功能,github上有专门的issue在跟踪这个,后续应该会有。issue上也有几个临时方案,目前我倾向使用chrome自带的来查看。在GUI打开的测试的浏览器中打开devtools,切到Sources, 按下cmd+shift+p(windows用户按ctrl+shift+p),输入coverage,选择重新刷新并统计代码执行覆盖率。

那么,high起来

为了高(撩)质(测)量(试)代(M)码(M),high起来。喜欢前端MM的可以手把手教起来了 (¬_¬)

本文章首发于本人公众号:枫之叶。有兴趣的可以长按下方二维码关注。^v^

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94439.html

相关文章

  • 端到端测试神器Cypress!进阶

    摘要:前言官方文档目前只支持英文,好处是官方入门视频很多,对于英文水平不好的也很容易入手优缺点优点只要你学好语法上应该不是很大问题获取类似,对前端同学来说是好处缺点内置的工具集成了谷歌内核,决定你只能是在谷歌浏览器上测试,但随着新版的内核采用内核 前言 官方文档:https://docs.cypress.io 目前只支持英文,好处是官方入门视频很多,对于英文水平不好的也很容易入手; 优缺点 ...

    oysun 评论0 收藏0
  • 端到端测试神器 cypress 浅入浅出

    摘要:我之前写过关于的文章,,最近在工作中用到比较多了,顿时觉得确实是比较优秀的一个。 我之前写过关于cypress的文章,https://segmentfault.com/a/11...,最近在工作中用到cypress比较多了,顿时觉得cypress确实是比较优秀的一个。 1. 软件安装. 2. 安装cypress 安装cypress客户端:http://download.cypress...

    CarlBenjamin 评论0 收藏0
  • 前端E2E测试框架 cypress了解一下

    摘要:是一款开箱即用可以跑在浏览器上的测试工具。同样,测试了也可以直接调试。这个时候我们的测试文件就可以访问了,点击之后发现他需要我们编写测试用例,那么接下来就手把手教你编写基本的测试用例。 What is E2E? 所谓的E2E就是end-to-end。 假设我们编写的每个功能程序都是一个黑匣子,最终用户也只会看到这个黑匣子,那么站在用户的角度来看并不需要知道这个黑匣子内部是什么东西也不...

    mushang 评论0 收藏0
  • 端到端测试哪家强?不容错过的Cypress

    摘要:阅读原文目前测试工具有哪些项目不需要不需要端到端测试一般都需要一个容器,来运行前端应用。向快速,一致和可靠的无剥落测试问好。 阅读原文 1. 目前E2E测试工具有哪些? 项目 Web Star puppeteer Chromium (~170Mb Mac, ~282Mb Linux, ~280Mb Win) 31906 nightmare Electron 15502 ...

    LancerComet 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<