Node 基础 2 :单元测试

144 阅读2分钟

1、引用jest

yarn add --dev jest

//package.json
{
  "scripts": {
    "test": "jest"
  }
}

2、建测试文件

__tests__ 文件夹放测试文件;

xxx.spec.js 测试文件名字一般用 spec ;

// db.spec.js

const db = require('../db.js')
describe('db',()=>{
  it('can read',()=>{
    expect(db.read instanceof Function ).toBe(true)
  })
  it('can write',()=>{
    expect(db.write instanceof Function).toBe(true)
  })
})

3、mock 数据

__test__ 文件夹

//db.spec.js    //名字一般用 spec

const db = require("../db.js")
const fs = require("fs")    //这是 node.js 的 fs 模块
jest.mock("fs")  //jest 接管了 fs 模块,后面的代码操作的都是 mock 后的 fs ;

describe("db", () => {
  //mock 完了之后,要清除 mock ,否则测试用例之间会相互干扰;
  //afterEach() 会在每一次 it 测试之后都执行
  //fs.clearMocks();
  
  afterEach(()=>{
    fs.clearMocks()
  })
  it("can read", async () => {
    const data = [{title: "hi", done: true}]
    //因为 db.read 调用的是 fs.readFile
    //通过 fs.setReadFileMock 改写 fs.readFile
    //使得任何人访问 '/xxx' ,得到的内容都是一个数组的 JSON 序列化,fs 只能读取字符串
    fs.setReadFileMock("/xxx", null, JSON.stringify(data))
    const list = await db.read("/xxx")
    expect(list).toStrictEqual(data)
  })

//测试写文件时,因为单元测试是不能和外界打交道的,所以不能真的写文件
//而是在写文件时,不把它写到文件里面,而是写在变量里面。

  it("can write", async () => {
    let fakeFile
    fs.setWriteFileMock("/yyy", (path,data,callback) => {
      fakeFile = data
      callback(null)
    })
    const list = [{title:'写博客',done:false},{title:'做项目',done:false}]
    await db.write(list,'/yyy')
    expect(fakeFile).toBe(JSON.stringify(list)+'\n')
  })
})

__mocks__文件夹

//fs.js

const fs = jest.createMockFromModule('fs');  //将 mock 出来的 fs 模块放在 fs 中
const _fs = jest.requireActual('fs')    // 将 node 的 fs 模块放在 _fs 中;


Object.assign(fs,_fs)  //将 _fs 复制到 fs ;
let readMocks = {}
fs.setReadFileMock = (path, error, data)=>{  //篡改 fs.readFile
  readMocks[path] = [error,data]
}

//fs.readFile(path,fn)  调用 fs.readFile 时,有可能只传了两个参数,需要判断下 callback === undefined;

fs.readFile = (path,options,callback)=>{  //fs 的其它属性都是复制 _fs 的,只改写了 fs.readFile();
  if(callback === undefined) callback = options
  if(path in readMocks){
    callback(...readMocks[path])    //如果是 mock 的数据,就调用 readMocks[path] 的第一个参数 error 和第二个参数 data;
  }else{
    _fs.readFile(path,options,callback)    //如果不是 mock 的数据,就调用 node.js 的 fs.readFile()
  }
}

let writeMocks = {}
fs.setWriteFileMock = (path,fn)=>{
  writeMocks[path] = fn
}

fs.writeFile = (path,data,options,callback) =>{
  if(path in writeMocks){
    writeMocks[path](path,data,options,callback)
  }else{
    _fs.writeFile(path,data,options,callback)
  }
}

//mock 完之后要清除 mock,否则测试用例之间会相互干扰
fs.clearMocks = ()=>{
  readMocks = {}
  writeMocks = {}
}

module.exports = fs;