GithubHelp home page GithubHelp logo

wwwzbwcom / stryker-mutator-example Goto Github PK

View Code? Open in Web Editor NEW
0.0 2.0 0.0 325 KB

Example for mutation testing JavaScript/TypeScript projects with Stryker Mutator and Jest

HTML 99.68% JavaScript 0.19% TypeScript 0.13%

stryker-mutator-example's Introduction

测试你的测试:Stryker Mutator + Jest 实现 JS / TS 变异测试

本文章所有示例项目代码均可在我的 GitHub 仓库查看

关于变异测试

前端工程化,单元测试,持续集成等近年来被越来越多的项目所接受,而我在学习前端的过程中,更是没怎么听说过变异测试相关的讯息(在学习的过程中,也鲜少看到中文社区具有相关的资料)。于是,这里就具体来讲讲变异测试是什么,我们怎么用 JavaScript 和 TypeScript 做变异测试。

什么是变异测试?变异测试的原理

所谓变异测试,就是通过修改我们代码中的一部分逻辑(比如将代码删除或逻辑取反),来生成一个 “变异” 的代码。然后在变异的代码上,再次运行我们的常规测试,检查常规测试能否检查出变异的存在(即变异后的代码能否通过测试)。

具体的,在这里我们可以看到 Stryker Mutator 的变异规则,这里是其中的一小部分:

以一个例子为例,如果我们的代码中,存在这样的语句:

if (a < b) {
    return a;
}

经过 a < ba >= b 的变异后,就会变成

if (a >= b) {
   return a;
}

Stryker Mutator 就是通过这些变异规则变异我们的源代码,再进行测试的。

为什么要做变异测试

变异测试的原理上并不复杂,不过为什么要进行变异测试呢?这是为了检查我们测试的健壮性。

虽然,我们常用代码覆盖率来检查我们的测试是否健壮,但代码覆盖率真的能反映问题吗?在编写单元测试的时候,只要运行过的代码就会被算入覆盖率的范围内,有时候,我们的断言 (expect) 并不能反映出问题(更极端的,甚至有时候我就忘记编写了断言,但是只要运行过,且没有发生异常,jest 就会认为我通过了测试,并只要我运行了足够多的代码,还会获得极高的测试覆盖率)。

具体的,举一个例子:

function max(a, b) {
	return a;
}

test('max()', () => {
	expect(max(2, 1)).toBe(2);
});

这样的代码,显然没有实现取 a,b 最大值的目的,但也显然依旧能通过测试,且由于程序代码每一行都被运行了,依旧能得到 100% 的覆盖率。

就如上面所说,我们的测试真的有效吗?这如果只靠测试覆盖率来判断,是完全不准确,甚至可以是根本和实际情况相反的。而如果进行了变异测试,变异测试程序就会发现在对我们代码进行了各种变异后(通过变异测试程序,加入 bug 后),如果依旧完好无损的通过了测试,直接提示我们,代码中的测试程序很可能存在严重的问题。

前端为什么要做变异测试?

前端为什么要做变异测试呢?前端的工作许多时候和业务联系十分紧密:例如,页面就常常会因为一些尝试性的调整(产品经理的喜好)发生大的变更。在这样的情况下,许多项目都选择甚至不做任何单元测试,通过简单的自证即可(另外的,前端出现问题要回滚,更新版本也较为容易)。

但是,随着前端涉及范围越来越广,许多基础设施类的库,工具等是很少发生变更的,并且依赖于他们的项目可能非常多,一旦这样的基础库没有做好测试,对生产环境将有很严重的影响。

当然,现在的 JavaScript / TypeScript 语言早也不仅仅局限在网页页面的范畴,可以想象在许多更为严谨的领域,一旦没有进行严谨的测试,后果将难以想象。例如 Space X 载人飞船的控制面板据传闻就利用了 Electron 进行开发,如果没有可靠的方法验证测试的完整性和程序的健壮性,可以想象宇航员们看到一个简单的页面错误时会有怎样惊恐的神情。

这些,无疑都说明了做变异测试是有意义的,那让我们来具体看看怎样做变异测试,并在具体的例子中看到它的工作原理吧:

开始搭建变异测试

初始化项目

首先,我们需要在我们的 npm 项目下安装相关的依赖,具体的,有 Stryker Mutator 本身的 CLI,核心库,jest 等:

npm i -D stryker-cli @stryker-mutator/core @stryker-mutator/jest-runner jest

而我们可以让 stryker mutator CLI 帮助我们初始化一个项目,运行以下命令,根据实际情况选择即可:

npx stryker init

例如,我选择的如下:(没有使用框架,使用 jest 运行测试,生成所有报告,用 npm 进行包管理,使用 JSON 配置文件)

Stryker Mutator + Jest 实现 JavaScript 变异测试

如果您是在已有项目中进行测试,可以略过这一步,但是,这里我们用一个简单的程序作为例子。

/src/index.js 下写入我们程序的函数:

/** /src/index.js */
function max(a, b) {
  if (a > b) {
    return a;
  }
  return b;
}

module.exports = { max };

/test/index.js 下写入我们测试该程序的用例:

/** /test/index.js */
const { max } = require("../src/index.js");

describe("Test max()", () => {
  test("max(1, 2) = 2", () => {
    expect(max(1, 2)).toBe(2);
  });
});

编写完成后,不要忘了运行一下测试,看看能否正确工作:

npx jest .

运行 Stryker Mutator

按照教程上的说明,我们只需要运行以下命令就可以进行测试了:

npx stryker run

最终,测试完成后,Stryker Mutator 会最终生成我们的变异测试报告:

可以看到,在我们的测试中,一共生成了 6 个变种,其中有两个都通过了我们的测试,有一个我们的测试没有覆盖到。

有可能运行测试时,一个变异都没有生成,最终的 score 将为 NaN,这可能是因为我们没有告诉 Stryker Mutator 对我们的哪些代码生成变异。

这个时候,我们需要在 stryker.conf.json 中写上 "mutate": ["<glob pattern>"],其中,<glob pattern> 是我们要进行变异的代码(也就是我们的源代码)所在的 glob pattern

例如,对于我们现在的项目,可以写为:

{
 "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
 "packageManager": "npm",
 "reporters": ["html", "clear-text", "progress", "dashboard"],
 "testRunner": "jest",
 "coverageAnalysis": "perTest",
 "mutate": ["./src/**"]
}

即对 ./src/ 下的所有文件进行变异

可以看到,我们的测试并不是很理想,具体没有通过测试的变种有以下三个:

具体的,对于这些变异为什么没有被正确的检测出来,我们用一个具体的例子来说明:当 Stryker Mutator 将我们的代码中的 if(a > b) 变异为 if(false)(第二个例子)后。程序在执行 max(1, 2) 时,a > bfalse,程序一样可以返回正确的结果 b

而因为我们的测试程序中,只测试了这种情况,导致这样的变异还是通过了测试,这就指示我们,测试并没有良好的覆盖这种情况,我们可能需要对测试程序进行改良和补充,这也就是变异测试的具体工作原理。

根据变异测试,修复测试漏洞

那么,既然变异测试为我们找到了用例中的这些问题了,就让我们来修复它吧:

/** /test/index.js */
const { max } = require("../src/index.js");

describe("Test max()", () => {
  test("max(1, 2) = 2", () => {
    expect(max(1, 2)).toBe(2);
  });

  test("max(2, 1) = 2", () => {
    expect(max(2, 1)).toBe(2);
  });

  test("max(1, 1) = 1", () => {
    expect(max(1, 1)).toBe(1);
  });
});

在此,我们补上了三个测试用例,重新运行测试可以看到,大部分变异都顺利被杀死了:

TypeScript 项目进行变异测试

顺道,我们也来讲一下 TypeScript 如何进行变异测试,这和 jest 运行 TypeScript 测试其实没有太大区别,让我们过一遍吧:

首先,安装相关依赖,这里我们使用 Babel 来转译 TypeScript 文件以便运行测试,这也是 Jest 官方推荐的方法

npm i -D @types/jest @babel/core @babel/preset-env @babel/preset-typescript

并且,我们需要在 babel.config.js 写入一下内容,让 jest 利用 Babel 根据这些配置进行转译:

// babel.config.js
module.exports = {
  presets: [
    ["@babel/preset-env", { targets: { node: "current" } }],
        "@babel/preset-typescript",
  ],
};

接下来,将 JavaScript 版本的项目程序和测试改写为 TypeScript:

项目源程序:

/** /src/index.ts */
export function max(a, b) {
  if (a > b) {
    return a;
  }
  return b;
}

测试程序:

/** /test/index.js */
import { max } from "../src/index";

describe("Test max()", () => {
  test("max(1, 2) = 2", () => {
    expect(max(1, 2)).toBe(2);
  });

  test("max(2, 1) = 2", () => {
    expect(max(2, 1)).toBe(2);
  });

  test("max(1, 1) = 1", () => {
    expect(max(1, 1)).toBe(1);
  });
});

通过 npx jest . 运行一下普通的测试来检查是否能正常工作。

顺利后,我们运行 npx stryker run 就可以进行变异测试了,正常情况下,我们会得到和 JavaScript 程序一样的运行结果,至此,我们就成功利用 Stryker Mutator 实现在 TypeScript 上的变异测试啦。

参考资料

stryker-mutator-example's People

Contributors

wwwzbwcom avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.