jest使用

一、起步 #

1、挂载

// 方法一
const Constructor = Vue.extend(monitor);
const vm = new Constructor();
// 方法二
const monitor = mount(monitor, { // 现在挂载组件,你便得到了这个包裹器
     mocks: { //模拟数据
         $store
     }
 });
const vm = monitor.vm;

2、生成jest.config.js配置文件

  • 安装相关依赖
npm install jest --global
jest --init
// 安装babel相关
npm install --save-dev babel-jest @babel/core @babel/preset-env
  • babel配置
// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
  ],
};
  • jest通过babel支持ts
npm install --save-dev @babel/preset-typescript
// 并在babel.config.js中添加
presets: [
  ...
  '@babel/preset-typescript',
  ...
  ],
  • 由于Babel对TypeScript的支持是编译,但是Jest在运行测试时不会进行类型检查。如果需要,可以使用ts-jest

二、常用匹配器 #

  • expect(完整列表)
  • 在此代码中,expect (2 + 2)返回一个"期望"的对象。 你通常不会对这些期望对象调用过多的匹配器。 在此代码中,.toBe(4)是匹配器。 当 Jest 运行时,它会跟踪所有失败的匹配器,以便它可以为你打印出很好的错误消息。
test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});
  • toBe用来测试精确相等,如果想要检查对象的值,使用toEqual代替,toEqual递归检查对象或数组的每个字段
  • 匹配相反的测试值
expect(a + b).not.toBe(0);
  • Truthiness:在测试中,你有时需要区分 undefined、 null,和 false,但有时你又不需要区分。 Jest 让你明确你想要什么。
    • toBeNull 只匹配 null
    • toBeUndefined 只匹配 undefined
    • toBeDefined 与 toBeUndefined 相反
    • toBeTruthy 匹配任何 if 语句为真
    • toBeFalsy 匹配任何 if 语句为假
    test('null', () => {
      const n = null;
      expect(n).toBeNull();
      expect(n).toBeDefined();
      expect(n).not.toBeUndefined();
      expect(n).not.toBeTruthy();
      expect(n).toBeFalsy();
    });
    test('zero', () => {
      const z = 0;
      expect(z).not.toBeNull();
      expect(z).toBeDefined();
      expect(z).not.toBeUndefined();
      expect(z).not.toBeTruthy();
      expect(z).toBeFalsy();
    });
    
  • 数字
test('two plus two', () => {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(3.5);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4.5);

  // toBe and toEqual are equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
});
// 对于浮点数相比较,使用toBeCloseTo而不是toEqual
test('两个浮点数字相加', () => {
  const value = 0.1 + 0.2;
  //expect(value).toBe(0.3);   这句会报错,因为浮点数有舍入误差
  expect(value).toBeCloseTo(0.3); // 这句可以运行
});
  • 字符串
// toMatch可以使用正则表达式去匹配字符串
test('there is no I in team', () => {
  expect('team').not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
  expect('Christoph').toMatch(/stop/);
});
  • 数组和可迭代,可以通过toContain来检查一个数组或可迭代对象是否包含某个特定项
const shoppingList = [
  'diapers',
  'kleenex',
  'trash bags',
  'paper towels',
  'beer',
];

test('the shopping list has beer on it', () => {
  expect(shoppingList).toContain('beer');
  expect(new Set(shoppingList)).toContain('beer');
});
  • 例外情况
    测试某个特定函数在调用时抛出错误,使用toThrow
function compileAndroidCode() {
  throw new Error('you are using the wrong JDK');
}

test('compiling android goes as expected', () => {
  expect(compileAndroidCode).toThrow();
  expect(compileAndroidCode).toThrow(Error);

  // You can also use the exact error message or a regexp
  expect(compileAndroidCode).toThrow('you are using the wrong JDK');
  expect(compileAndroidCode).toThrow(/JDK/);
});  

三、测试异步代码 #

在JavaScript中执行异步代码是很常见的。 当你有以异步方式运行的代码时,Jest 需要知道当前它测试的代码是否已完成,然后它可以转移到另一个测试。 Jest有若干方法处理这种情况。

  • 回调
    最常见的异步模式是回调函数。
    例如,假设您有一个fetchData(callback)函数,获取一些数据并在完成时调用callback(data)。你想要测试此返回的数据是否为字符串'peanut butter'.
    默认情况下,Jest 测试一旦执行到末尾就会完成。 那意味着该测试将不会按预期工作:
// 不要这样做!
test('the data is peanut butter', () => {
  function callback(data) {
    expect(data).toBe('peanut butter');
  }

  fetchData(callback);
});

问题在于一旦fetchData执行结束,此测试就在没有调用回调函数前结束。
还有另一种形式的test,解决此问题。 使用单个参数调用 done,而不是将测试放在一个空参数的函数。 Jest会等done回调函数执行结束后,结束测试。

test('the data is peanut butter', done => {
  function callback(data) {
    expect(data).toBe('peanut butter');
    done();
  }

  fetchData(callback);
});

如果 done()永远不会调用,这个测试将失败,这也是你所希望发生的。

  • Promises
    如果你的代码使用Promise,则有一种更简单的方法来处理异步测试。从测试中返回一个承诺,Jest将等待该承诺解决。如果承诺被拒绝,则测试将自动失败。
    举个例子,如果fetchData不使用回调函数,而是返回一个 Promise,其解析值为字符串'peanut butter'我们可以这样测试:
// 测试异步调用
function fetchData(resolve, reject) {
  setTimeout(() => {
    return resolve("peanut butter");
  }, 2000);
}
let fetch1 = new Promise(fetchData);
test('the data is peanut butter', () => {
  return fetch1.then(data => {
    expect(data).toBe('peanut butter');
  });
});
  • .resolves / .rejects
    您也可以在 expect 语句中使用.resolves匹配器,Jest 将等待此 Promise 解决。 如果承诺被拒绝,则测试将自动失败。
test('the data is peanut butter', () => {
  return expect(fetchData()).resolves.toBe('peanut butter');
});

一定不要忘记把整个断言作为返回值返回⸺如果你忘了return语句的话,在 fetchData 返回的这个 promise 变更为 resolved 状态、then() 有机会执行之前,测试就已经被视为已经完成了。

如果你想要 Promise 被拒绝,使用 .catch 方法。 它参照工程 .resolves 匹配器。 如果 Promise 被拒绝,则测试将自动失败。

test('the fetch fails with an error', () => {
  return expect(fetchData()).rejects.toMatch('error');
});
  • Async/Await
    可以在测试中使用 async 和 await,要编写异步测试,请在传递给测试的函数前面使用async关键字
test('the data is peanut butter', async () => {
  const data = await fetchData();
  expect(data).toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch('error');
  }
});

同样,可以将async和await与.resolves或.rejects结合使用。

test('the data is peanut butter', async () => {
  await expect(fetchData()).resolves.toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
  await expect(fetchData()).rejects.toThrow('error');
});

在这些情况下,async和await实际上是语法糖,与应诺示例使用的逻辑相同。
上述的诸多形式中没有哪个形式特别优于其他形式,你可以在整个代码库中,甚至也可以在单个文件中混合使用它们。 这只取决于哪种形式更能使您的测试变得简单。

三、Setup and Teardown #

写测试的时候你经常需要在运行测试前做一些准备工作,和在运行测试后进行一些整理工作。 Jest 提供辅助函数来处理这个问题。

  • 为多次测试重复设置
    如果你有一些要为多次测试重复设置的工作,你可以使用 beforeEach 和 afterEach。
    例如,我们考虑一些与城市信息数据库进行交互的测试。 你必须在每个测试之前调用方法 initializeCityDatabase() ,同时必须在每个测试后,调用方法 clearCityDatabase()。 你可以这样做:
beforeEach(() => {
  initializeCityDatabase();
});

afterEach(() => {
  clearCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});

beforeEach和afterEach能够通过与 异步代码测试 相同的方式处理异步代码 — — 他们可以采取 done 参数或返回一个 promise。 例如,如果 initializeCityDatabase() 返回解决数据库初始化时的 promise ,我们会想返回这一 promise︰

beforeEach(() => {
  return initializeCityDatabase();
});
  • 一次性设置
    在某些情况下,你只需要在文件的开头做一次设置。当设置异步时,这尤其麻烦,因此您不能内联. Jest 提供beforeAllafterAll处理这种情况。
    例如,如果initializeCityDatabaseclearCityDatabase都返回了 promise ,城市数据库可以在测试中重用,我们就能把我们的测试代码改成这样:
beforeAll(() => {
  return initializeCityDatabase();
});
afterAll(() => {
  return clearCityDatabase();
});
test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});
  • 作用域
    默认情况下,before 和 after 的块可以应用到文件中的每个测试。 此外可以通过 describe 块来将测试分组。 当 before 和 after 的块在 describe 块内部时,则其只适用于该 describe 块内的测试
    注意,顶级的 beforeEach 在 describe 块级的 beforeEach 之前被执行。 这可能有助于说明所有钩子的执行顺序
beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));
test('', () => console.log('1 - test'));
describe('Scoped / Nested block', () => {
  beforeAll(() => console.log('2 - beforeAll'));
  afterAll(() => console.log('2 - afterAll'));
  beforeEach(() => console.log('2 - beforeEach'));
  afterEach(() => console.log('2 - afterEach'));
  test('', () => console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll
  • desribe和test块的执行顺序
    Jest 会在所有真正的测试开始之前执行测试文件里所有的describe处理程序(handlers)。 这是在before*after*处理程序里面 (而不是在 describe 块中)进行准备工作和整理工作的另一个原因。 当 describe 块运行完后,,默认情况下,Jest 会按照 test 出现的顺序(译者注:原文是in the order they were encountered in the collection phase)依次运行所有测试,,等待每一个测试完成并整理好,然后才继续往下走
  • 通用建议
    如果测试失败,第一件要检查的事就是,当仅运行这条测试时,它是否仍然失败。要仅使用Jest运行一个测试,请将该test命令临时更改为test.only
test.only('this will be the only test that runs', () => {
  expect(true).toBe(false);
});

test('this test will not run', () => {
  expect('A').toBe('A');
});

如果你有一个测试,当它作为一个更大的用例中的一部分时,经常运行失败,但是当你单独运行它时,并不会失败,所以最好考虑其他测试对这个测试的影响。 通常可以通过修改 beforeEach 来清除一些共享的状态来修复这种问题。如果不确定是否正在修改某些共享状态,也可以尝试使用beforeEach记录数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容