Mock系统接口

发表于:2017-7-27 11:47

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:卢祎    来源:知乎专栏

  2. Mock事件发射器
  模拟node的child_process.spawn方法略微有点复杂,因为它返回ChildProcess事件发射器。宝宝不用怕,我已经写了一个实用程序来stubspawn或者exec这些系统方法了。另外,我还打造了一件让测试更加友好的神器stub-spawn-once——每用一次,它都会自动清除掉stub。
  我给你们打个样,看好喽~
  ./blame.js用以下命令调用./exec.js(假设文件名是foo.txt,文件内容为10行)
  git blame --porcelain -L 10,10 foo.txt
  上述命令就会输出以下结果
  6f272d6aef8a1d19e559792d9493d07d4218ea09 10 10 1
  author Gleb Bahmutov
  author-mail <gleb.bahmutov@gmail.com>
  author-time 1499625750
  author-tz -0400
  committer Gleb Bahmutov
  committer-mail <gleb.bahmutov@gmail.com>
  committer-time 1499625750
  committer-tz -0400
  summary this is initial commit
  boundary
  filename foo.txt
  接下来, 我们在测试时,就可以mockfs.existsSync和child_process.exec这些Node内置接口方法,如下所示
  const sinon = require('sinon')
  // we need access to "fs" object
  const fs = require('fs')
  const {stubExecOnce} = require('stub-spawn-once')
  // common-tags is a great library for dealing with whitespace
  // in ES6 template literals
  const {stripIndent} = require('common-tags')
  describe('blame', () => {
    const filename = 'foo.txt'
    const line = 10
    beforeEach(() => {
      sinon.stub(fs, 'existsSync').withArgs(filename).returns(true)
      const cmd = 'git blame --porcelain -L 10,10 foo.txt'
      const blameOutput = stripIndent`
  // the mock output shown above
      `
      stubExecOnce(cmd, blameOutput)
    })
    afterEach(() => {
  // remove sinon's stubs
      fs.existsSync.restore()
  // stubExecOnce removes itself after single use
    })
    it('returns blame information for given line', () => {
      return blame(filename, line)
        .then(info => {
  // check info object
        })
    })
  })
  注意,现在我们已经不再关心或者依赖我们的内部模块了。取而代之的是,我们只关心和操作系统的交互。~duang~这样就比较容易把控啦。试想一下,一个开发者更新了./blame.js的测试,然后他就可以看到期望的git命令和mock输出结果,甚至还可以在终端执行命令。(貌似很酸爽哦~)
  而且如果我们改变内部./exec.js 的代码,或者用其他库来替换它去执行shell脚本,我们测试依然执行同样的操作系统方法调用,应该也可以解析出同样的命令输出。现在对./blame.js的测试只要测试一个源文件就好,不用测试好几个了。
  示例之Mock系统接口
  走过路过不要错过,各位客官,来看看这些规范文件,它们介绍了如何mock稳健的NODE接口调用方法,再也不用费尽心神地去研究那些内部模块喽。
  ggit/blame-spec.js
  snap-shot 和schema-shot测试(见下面的福利篇)
  cypress-io/env-or-json-file
  如下stub文件下载
sinon.stub(fs, 'existsSync').withArgs(fullPath).returns(true)
  sinon
    .stub(fs, 'readFileSync')
    .withArgs(fullPath, 'utf8')
    .returns(configString)
  generator-node-bahmutov
  看完用nock来stub HTTP(S)请求,只想说那句经典台词 so easy~~ haha~~
  const nock = require('nock')
  const description = 'cool project, bro'
  beforeEach(() => {
    nock('https://api.github.com')
      .get('/repos/no-such-user/does-not-exist')
      .reply(200, { description })
  })
  it('can mock non-existent repo', () => {
    const url = 'git@github.com:no-such-user/does-not-exist.git' return repoDescription(url).then(text => {
      la(description === text, 'wrong description returned', text)
    })
  })
  每一个nock拦截都会在每次使用后自动删除,所以我们就不用费精力考虑清除喽~
  更多案例,请戳这里node-mock-examples
  总结思考
  mock内部模块是很难滴:他们由Node模块系统缓存,不稳定,而且没有文档(两眼一抹黑啊)
  mock操作系统很简单哦;接口都很稳健,而且都文档化了
  有sinon和stub-spawn-once 这些库让stub接口方法 so easy,妈妈再也不用担心了~~
  福利篇
  我喜欢用snapshot testing快速断言所有接收到的结果。只需要包装从blame(filename, line)返回的promise就可以了。
  const snapshot = require('snap-shot')
  describe('blame', () => {
    beforeEach(() => {
      // ...
    })
    it('returns blame information for given line', () =>
      snapshot(blame(filename, line))
    })
  })
  同理,在这种情况下,我们可以通过schema snapshots跳过mock操作系统结果,相较于确切的数值,我们对结果的模式更感兴趣。
  const schemaShot = require('schema-shot')
  describe('blame', () => {
    // no setup is necessary!
    it('returns blame information for this file', () =>
      schemaShot(blame(__filename, 1))
    })
  })
  /*
  saves schema:
  $ cat __snapshots__/blame-spec.js.schema-shot
  exports['gets blame for 1 line of this file 1'] = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
      "commit": {
        "type": "string",
        "required": true
      },
      "author": {
        "type": "string",
        "required": true
      },
      "committer": {
        "type": "string",
        "required": true
      },
      "summary": {
        "type": "string",
        "required": true
      },
      "filename": {
        "type": "string",
        "required": true
      },
      "line": {
        "type": "string",
        "required": true
      }
    },
    "additionalProperties": false,
    "list": false,
    "example": {
      "commit": "adfb30d5888bb1eb9bad1f482248edec2947dab6",
      "author": "Gleb Bahmutov",
      "committer": "Gleb Bahmutov",
      "summary": "move blame test",
      "filename": "spec/blame-spec.js",
      "line": "\tconst la = require('lazy-ass');"
    }
  }
  */
  Happy testing !
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号