Node全解03_01jest

单元测试 JEST使用

代码使用我们上次写的Node命令行工具

如何测试

步骤

  • 选择测试工具: jest
  • 设计测试用例
  • 写测试,运行测试,改代码
  • 单元测试、功能测试、集成测试

重点

  • 单元测试不应该和外界打交道(那是集成测试做的) 应该 mock
    • 不能操作硬盘
    • 不能操作网络
    • 不能操作任何东西,只能操作你自己
  • 单元测试的对象是函数
  • 功能测试的对象是模块
  • 集成测试的对象是系统
  • 我们先搞定单元测试

直接 CRM学习法

  • Jest官网 Docs
  • yarn add --dev jest
  • 修改package.json

    1
    2
    3
    "scripts": {
    "test": "jest"
    },
  • 新建 __test__ 目录 并在它下面创建 db.spec.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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)
    })
    })
  • 运行 npm run test

如何测试读文件呢?

如果你想这样测试!!! 请放弃

  • 不该和外界打交道,万一你的文件已经存在了呢?这样就会有干扰
1
2
fs.writeFileSync('/xxx.json',`[{"title":"hi","done":true}]`)
db.read('/xxx.json')

请搜索 jest mock fs jest doc 的 Manual Mocks章节

1
jest.mock('fs') // 意思是 jest 接管 fs模块

Mock fs 模块

  • 新建 __mocks__/fs.js 代表 mock 一个假的 fs模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 声明是 fs的 jest 的假模块
    const fs = jest.genMockFromModule('fs');

    fs.x = ()=>{
    console.log('hi')
    return 'xxx';
    }

    module.exports = fs;
  • db.spec.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const db = require("../db.js")
    const fs = require('fs');
    jest.mock('fs');

    describe("db",()=>{
    it("can read",()=>{
    expect(fs.x()).toBe('xxx')
    })
    })
  • 运行 npm run test 成功

完整版 mock fs

__mocks__/fs.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 声明是 fs的 jest 的假模块
const fs = jest.genMockFromModule('fs');
const _fs = jest.requireActual('fs');

Object.assign(fs,_fs);

let readMocks = {};

fs.setReadFileMock = (path, error, data)=>{
readMocks[path] = [error,data];
}

// 覆盖 fs的 readFile()
fs.readFile = (path, options, callBack) => {
// 如果你 fs.readFile('xxx',fn) 代表第二个参数是 callBack
if(callBack === undefined){
callBack = options
}

if(path in readMocks){
callBack(...readMocks[path])
}else{
_fs.readFile(path, options, callBack)
}
}

let writeMocks = {}

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

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

}

fs.clearMocks = ()=>{
readMocks = {};
writeMocks = {};
}

module.exports = fs;

__tests__/db.spec.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const db = require("../db.js")
const fs = require('fs');
jest.mock('fs');

describe("db",()=>{
afterEach(()=>{
// 每个it 执行之后
fs.clearMocks();
})
it("can read", async () =>{
const data = [{title:"hi",done:true}]
fs.setReadFileMock('/xxx',null,JSON.stringify(data));
const list = await db.read('/xxx');
expect(list).toStrictEqual(data)
})

it("can wriete", async () =>{
let fakeFile;
fs.setWriteFileMock('/yyy',(path, data, options, callBack)=>{
fakeFile = data;
callBack(null)
})
const list = [{title:"见欧阳娜娜",done:true}, {title:"见迪丽热巴",done:true}]
await db.write(list,'/yyy');
expect(fakeFile).toBe(JSON.stringify(list) + "\n");
})
})

代码仓库