mock vs stub vs spy
Sinon 是用于 JavaScript 的测试框架,适用于任何单元测试框架。
Sinon 将测试为三种类型:
Spies:提供有关函数调用的信息,而不会影响其行为
Stubs:类似于 Spies,但完全取代了功能。这样就可以使存根函数做任何你喜欢的事情 - 抛出异常,返回一个特定的值等等
Mocks:通过组合 Spies 和 Stubs,可以更轻松地替换整个对象
spy、stub、mock 用法
假设我们有 Todo.ts 代码如下。
// Todo.ts
export default class Todo {
private todos: string[] = [];
private ajax: any;
constructor(ajax?) {
ajax && this.ajax = ajax;
}
add(content) {
this.todos.push(content);
}
printFirstTodo() {
console.log(this.todos[0]);
}
syncClound() {
this.ajax && this.ajax.get("/todo.json", data => {
this.todos = data;
});
}
}
然后我们使用 Sinon 来为它做单元测试。分别使用 spy、stub、mock 来做单元测试。
spy
// Todo.test.ts
import * as sinon from "sinon";
// 引入 chai 作为断言库
import { expect } from "chai";
import Todo from "./Todo";
describe("测试 Todo", () => {
it("spy print", () => {
const t = new Todo();
// 用 sinon.spy 监控 console.log 函数的执行,并不替换其原有的实现
sinon.spy(console, "log");
t.add("Go to school");
t.add("Sleep");
t.printFirstTodo();
// @ts-ignore console.log.calledOnce 无法识别
// 如果 spy 监控的 console.log 函数刚好被调用一次,则返回 true
expect(console.log.calledOnce).to.be.true;
});
});
stub
// Todo.test.ts
import * as sinon from "sinon";
// 引入 chai 作为断言库
import { expect } from "chai";
import Todo from "./Todo";
describe("测试 Todo", () => {
it("stub syncTodoFromCloud", () => {
const t = new Todo();
// 用 stub 模拟 t.add 函数,stubAdd 函数被模拟为一个空函数
const stubAdd = sinon.stub(t, "add").callsFake(() => {});
// 执行被模拟的 stubAdd 函数,这时候 'Go to school' 并没有被真正地 push
stubAdd("Go to school");
// 尝试打印,会打印出 undefined
t.printFirstTodo();
// 我们期望 stubAdd 被执行了一次
expect(stubAdd.calledOnce).to.be.true;
});
});
mock
// Todo.test.ts
import * as sinon from "sinon";
// 引入 chai 作为断言库
import { expect } from "chai";
import Todo from "./Todo";
describe("测试 Todo", () => {
it("mock syncTodoFromCloud", () => {
const t = new Todo();
// 这时候 console 已经被 mock 完全 mock 了
// 这里可以调用console下的任何方法,但并不会执行
const mock = sinon.mock(console);
// 由于 console 已经完全被 mock了,所以我们这里可以提前描述我们预期的行为
mock.expects("log").calledOnce;
t.add("Go to school");
t.printFirstTodo();
// 校验
mock.verify();
});
});
测试结果
最后我们用一张图来总结一下三者的区别