前端测试框架Jest系列教程 -- Expect(验证)

写在前面

在编写测试时,我们通常需要检查值是否满足某些条件,Jest中提供的expect允许你访问很多“Matchers”,这些“匹配器”允许您验证不同的东西。

Expect 可以验证什么

Jest中提供了如下的验证方法:

expect(value)
expect.extend(matchers)
expect.anything()
expect.any(constructor)
expect.arrayContaining(array)
expect.assertions(number)
expect.hasAssertions()
expect.not.arrayContaining(array)
expect.not.objectContaining(object)
expect.not.stringContaining(string)
expect.not.stringMatching(string | regexp)
expect.objectContaining(object)
expect.stringContaining(string)
expect.stringMatching(string | regexp)
expect.addSnapshotSerializer(serializer)
.not
.resolves
.rejects
.toBe(value)
.toHaveBeenCalled()
.toHaveBeenCalledTimes(number)
.toHaveBeenCalledWith(arg1, arg2, ...)
.toHaveBeenLastCalledWith(arg1, arg2, ...)
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
.toHaveReturned()
.toHaveReturnedTimes(number)
.toHaveReturnedWith(value)
.toHaveLastReturnedWith(value)
.toHaveNthReturnedWith(nthCall, value)
.toBeCloseTo(number, numDigits)
.toBeDefined()
.toBeFalsy()
.toBeGreaterThan(number)
.toBeGreaterThanOrEqual(number)
.toBeLessThan(number)
.toBeLessThanOrEqual(number)
.toBeInstanceOf(Class)
.toBeNull()
.toBeTruthy()
.toBeUndefined()
.toContain(item)
.toContainEqual(item)
.toEqual(value)
.toHaveLength(number)
.toMatch(regexpOrString)
.toMatchObject(object)
.toHaveProperty(keyPath, value)
.toMatchSnapshot(propertyMatchers, snapshotName)
.toStrictEqual(value)
.toThrow(error)
.toThrowErrorMatchingSnapshot()

下面我们将介绍部分验证的使用场景:

expect(value)

每当您希望测试一个值时,都会使用expect函数。你很少会调用expect本身。相反你将使用expect和“matcher”函数来断言关于值的某些内容。更容易理解这个例子。假设您有一个方法bestLaCroixFlavor(),它应该返回字符串“柚子”。下面是测试方法:

test(‘the best flavor is grapefruit‘, () => {
  expect(bestLaCroixFlavor()).toBe(‘grapefruit‘);
});

在上面的case中,toBe是matcher函数。为了帮助你测试不同的东西,Jest中有很多不同的matcher函数。

expect的参数应该是代码生成的值,而匹配程序的任何参数都应该是正确的值。如果你将它们混合在一起,那么你的测试仍然可以工作,但是失败测试的错误消息看起来会很奇怪。

expect.extend(matchers)

你可以使用expect.extend将自己的matcher添加到Jest中。例如假设你正在测试一个 theory library,并且你经常断言数字可以被其他数整除,你可以把它抽象成toBeDivisibleBy matcher:

expect.extend({
  toBeDivisibleBy(received, argument) {
    const pass = received % argument == 0;
    if (pass) {
      return {
        message: () =>
          `expected ${received} not to be divisible by ${argument}`,
        pass: true,
      };
    } else {
      return {
        message: () => `expected ${received} to be divisible by ${argument}`,
        pass: false,
      };
    }
  },
});

test(‘even and odd numbers‘, () => {
  expect(100).toBeDivisibleBy(2);
  expect(101).not.toBeDivisibleBy(2);
  expect({apples: 6, bananas: 3}).toEqual({
    apples: expect.toBeDivisibleBy(2),
    bananas: expect.not.toBeDivisibleBy(2),
  });
});

expect.extends还支持异步匹配器。异步匹配器返回一个promise,因此你需要等待返回的值。让我们使用一个示例matcher来说明它们的用法。我们要实现一个非常相似的matcher,而不是toBeDivisibleBy,唯一的区别是可分割的数字将从外部源中提取。 

expect.extend({
  async toBeDivisibleByExternalValue(received) {
    const externalValue = await getExternalValueFromRemoteSource();
    const pass = received % externalValue == 0;
    if (pass) {
      return {
        message: () =>
          `expected ${received} not to be divisible by ${externalValue}`,
        pass: true,
      };
    } else {
      return {
        message: () =>
          `expected ${received} to be divisible by ${externalValue}`,
        pass: false,
      };
    }
  },
});

test(‘is divisible by external value‘, async () => {
  await expect(100).toBeDivisibleByExternalValue();
  await expect(101).not.toBeDivisibleByExternalValue();
});

匹配器应该返回带有两个键的对象(或对象的promise)。pass指示是否存在匹配,message提供了一个没有参数的函数,在失败时返回错误消息。因此当pass为false时,当expect(x). yourmatcher()失败时,消息应该返回错误消息。当pass为true时,消息应该返回expect(x).no . yourmatcher()失败时的错误消息。

这些辅助函数可以在自定义匹配器中找到: this.isNot,返回一个布尔值,让你知道这个匹配器是用否定的.not修饰符调用的,允许你翻转断言。

this.equals(a, b)

如果两个对象具有相同的值(递归地),则返回true。

this.utils有很多有用的工具。utils主要由来自jest-matcher-utils的导出组成。最有用的是matcherHint、printExpected和printReceived,它们可以很好地格式化错误消息。例如看看toBe matcher的实现:

const diff = require(‘jest-diff‘);
expect.extend({
  toBe(received, expected) {
    const pass = Object.is(received, expected);

    const message = pass
      ? () =>
          this.utils.matcherHint(‘.not.toBe‘) +
          ‘\n\n‘ +
          `Expected value to not be (using Object.is):\n` +
          `  ${this.utils.printExpected(expected)}\n` +
          `Received:\n` +
          `  ${this.utils.printReceived(received)}`
      : () => {
          const diffString = diff(expected, received, {
            expand: this.expand,
          });
          return (
            this.utils.matcherHint(‘.toBe‘) +
            ‘\n\n‘ +
            `Expected value to be (using Object.is):\n` +
            `  ${this.utils.printExpected(expected)}\n` +
            `Received:\n` +
            `  ${this.utils.printReceived(received)}` +
            (diffString ? `\n\nDifference:\n\n${diffString}` : ‘‘)
          );
        };

    return {actual: received, message, pass};
  },
});  

打印结果如下:

 expect(received).toBe(expected)

    Expected value to be (using Object.is):
      "banana"
    Received:
      "apple"

当断言失败时,错误消息应该向用户提供必要的尽可能多的信号,以便用户能够快速地解决问题。你应该编写一个精确的失败消息,以确保自定义断言的用户具有良好的开发经验。

expect.anything()

它匹配除null或undefined之外的任何内容。你可以在内部使用toEqual或toBeCalledWith而不是文字值。例如如果你想检查一个模拟函数是否被调用,它的参数是非空的:

test(‘map calls its argument with a non-null argument‘, () => {
  const mock = jest.fn();
  [1].map(x => mock(x));
  expect(mock).toBeCalledWith(expect.anything());
});

expect.any(constructor)

匹配给定构造函数所创建的任何内容。你可以在内部使用toEqual或toBeCalledWith而不是文字值。如果你想检查一个模拟函数是否被调用时带有一个数字:

function randocall(fn) {
  return fn(Math.floor(Math.random() * 6 + 1));
}

test(‘randocall calls its callback with a number‘, () => {
  const mock = jest.fn();
  randocall(mock);
  expect(mock).toBeCalledWith(expect.any(Number));
});

expect.arrayContaining(array) 

匹配一个接收到的数组,该数组包含预期数组中的所有元素。也就是说预期数组是接收数组的子集。因此它匹配一个接收到的数组,该数组包含不属于预期数组的元素。

你可以用它代替文字的值: toEqual或toBeCalledWith

describe(‘arrayContaining‘, () => {
  const expected = [‘Alice‘, ‘Bob‘];
  it(‘matches even if received contains additional elements‘, () => {
    expect([‘Alice‘, ‘Bob‘, ‘Eve‘]).toEqual(expect.arrayContaining(expected));
  });
  it(‘does not match if received does not contain expected elements‘, () => {
    expect([‘Bob‘, ‘Eve‘]).not.toEqual(expect.arrayContaining(expected));
  });
});

  

describe(‘Beware of a misunderstanding! A sequence of dice rolls‘, () => {
  const expected = [1, 2, 3, 4, 5, 6];
  it(‘matches even with an unexpected number 7‘, () => {
    expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
      expect.arrayContaining(expected)
    );
  });
  it(‘does not match without an expected number 2‘, () => {
    expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual(
      expect.arrayContaining(expected),
    );
  });
});

expect.assertions(number)

验证在测试期间调用了一定数量的断言。在测试异步代码时这通常很有用,以便确保回调中的断言确实被调用。

假设我们有一个函数doAsync,它接收两个回调callback1和callback2,它将异步地以一个未知的顺序调用它们。我们可以用:

test(‘doAsync calls both callbacks‘, () => {
  expect.assertions(2);
  function callback1(data) {
    expect(data).toBeTruthy();
  }
  function callback2(data) {
    expect(data).toBeTruthy();
  }

  doAsync(callback1, callback2);
});

expect.hasAssertions()

验证在测试期间至少调用了一个断言。在测试异步代码时,这通常很有用以便确保回调中的断言确实被调用。

假设我们有一些处理状态的函数。prepareState调用一个状态对象的回调,validateState运行在那个状态对象上,waitOnState返回一个承诺,直到所有prepareState回调完成。我们可以用:

test(‘prepareState prepares a valid state‘, () => {
  expect.hasAssertions();
  prepareState(state => {
    expect(validateState(state)).toBeTruthy();
  });
  return waitOnState();
});

expect.not.arrayContaining(array)

匹配所接收的数组,该数组不包含预期数组中的元素。也就是说,预期的数组不是接收数组的子集。它与 expect.arrayContaining 相反

describe(‘not.arrayContaining‘, () => {
  const expected = [‘Samantha‘];

  it(‘matches if the actual array does not contain the expected elements‘, () => {
    expect([‘Alice‘, ‘Bob‘, ‘Eve‘]).toEqual(
      expect.not.arrayContaining(expected),
    );
  });
});

expect.not.objectContaining(object) 

匹配任何未递归地匹配预期属性的接收对象。也就是说预期对象不是接收对象的子集。因此,它匹配所接收的对象,该对象包含不属于预期对象的属性。它与expect. objectcontains相反。

describe(‘not.objectContaining‘, () => {
  const expected = {foo: ‘bar‘};

  it(‘matches if the actual object does not contain expected key: value pairs‘, () => {
    expect({bar: ‘baz‘}).toEqual(expect.not.objectContaining(expected));
  });
});

expect.not.stringContaining(string)

匹配不包含确切期望字符串的接收字符串。它与expect.stringContaining.相反

describe(‘not.stringContaining‘, () => {
  const expected = ‘Hello world!‘;

  it(‘matches if the actual string does not contain the expected substring‘, () => {
    expect(‘How are you?‘).toEqual(expect.not.stringContaining(expected));
  });
});

expect.not.stringMatching(string | regexp)

匹配不匹配预期regexp的接收字符串。它与expect.stringMatching.相反

describe(‘not.stringMatching‘, () => {
  const expected = /Hello world!/;

  it(‘matches if the actual string does not match the expected regex‘, () => {
    expect(‘How are you?‘).toEqual(expect.not.stringMatching(expected));
  });
});

expect.objectContaining(object) 

匹配递归地匹配预期属性的任何接收对象。也就是说,预期对象是接收对象的子集。因此,它匹配所接收的对象,该对象包含不属于预期对象的属性。

与期望对象中的文字属性值不同,您可以使用matchers、expect.anything()等等。

假设我们希望使用事件对象调用onPress函数,我们需要验证的是事件是否有event.x属性和y属性。我们可以这样做:

test(‘onPress gets called with the right thing‘, () => {
  const onPress = jest.fn();
  simulatePresses(onPress);
  expect(onPress).toBeCalledWith(
    expect.objectContaining({
      x: expect.any(Number),
      y: expect.any(Number),
    }),
  );
});

expect.stringMatching(string | regexp)

匹配与预期regexp匹配的接收字符串。你可以用它代替文字的值:

1. 在toEqual或toBeCalledWith
2. 匹配arraycontains中的元素
3. 匹配objectContaining 或者toMatchObject的属性

这个示例还展示了如何使用expect嵌套多个不对称的匹配器。在expect.arrayContaining stringMatching。

describe(‘stringMatching in arrayContaining‘, () => {
  const expected = [
    expect.stringMatching(/^Alic/),
    expect.stringMatching(/^[BR]ob/),
  ];
  it(‘matches even if received contains additional elements‘, () => {
    expect([‘Alicia‘, ‘Roberto‘, ‘Evelina‘]).toEqual(
      expect.arrayContaining(expected),
    );
  });
  it(‘does not match if received does not contain expected elements‘, () => {
    expect([‘Roberto‘, ‘Evelina‘]).not.toEqual(
      expect.arrayContaining(expected),
    );
  });
});

.toBe(value)

toBe只是检查一个值是否符合您的期望。它使用对象。是要检查完全相等。例如此代码将验证can对象的一些属性:

const can = {
  name: ‘pamplemousse‘,
  ounces: 12,
};

describe(‘the can‘, () => {
  test(‘has 12 ounces‘, () => {
    expect(can.ounces).toBe(12);
  });

  test(‘has a sophisticated name‘, () => {
    expect(can.name).toBe(‘pamplemousse‘);
  });
});

.toEqual(value)

如果要检查两个对象是否具有相同的值,请使用. toequal。此matcher递归地检查所有字段的相等性,而不是检查对象标识——这也称为“深度相等”。例如,toEqual和toBe在这个测试套件中表现不同,所以所有的测试都通过:

const can1 = {
  flavor: ‘grapefruit‘,
  ounces: 12,
};
const can2 = {
  flavor: ‘grapefruit‘,
  ounces: 12,
};

describe(‘the La Croix cans on my desk‘, () => {
  test(‘have all the same properties‘, () => {
    expect(can1).toEqual(can2);
  });
  test(‘are not the exact same can‘, () => {
    expect(can1).not.toBe(can2);
  });
});

.toMatchObject(object)

使用. tomatchobject检查一个JavaScript对象是否匹配一个对象的属性子集。它将把接收到的对象与预期对象中没有的属性匹配起来。

您还可以传递一个对象数组,在这种情况下,只有当接收到的数组中的每个对象(在上面描述的番茄对象意义中)与预期数组中的相应对象相匹配时,该方法才会返回true。如果想要检查两个数组在它们的元素数量上是否匹配,而不是arrayinclude,这是非常有用的,因为它允许在接收的数组中添加额外的元素。

可以将属性匹配到值或匹配项。

const houseForSale = {
  bath: true,
  bedrooms: 4,
  kitchen: {
    amenities: [‘oven‘, ‘stove‘, ‘washer‘],
    area: 20,
    wallColor: ‘white‘,
  },
};
const desiredHouse = {
  bath: true,
  kitchen: {
    amenities: [‘oven‘, ‘stove‘, ‘washer‘],
    wallColor: expect.stringMatching(/white|yellow/),
  },
};

test(‘the house has my desired features‘, () => {
  expect(houseForSale).toMatchObject(desiredHouse);
});
describe(‘toMatchObject applied to arrays arrays‘, () => {
  test(‘the number of elements must match exactly‘, () => {
    expect([{foo: ‘bar‘}, {baz: 1}]).toMatchObject([{foo: ‘bar‘}, {baz: 1}]);
  });

  // .arrayContaining "matches a received array which contains elements that
  // are *not* in the expected array"
  test(‘.toMatchObject does not allow extra elements‘, () => {
    expect([{foo: ‘bar‘}, {baz: 1}]).toMatchObject([{foo: ‘bar‘}]);
  });

  test(‘.toMatchObject is called for each elements, so extra object properties are okay‘, () => {
    expect([{foo: ‘bar‘}, {baz: 1, extra: ‘quux‘}]).toMatchObject([
      {foo: ‘bar‘},
      {baz: 1},
    ]);
  });
});

.toHaveProperty(keyPath ,value)

使用. tohaveproperty检查在提供的引用keyPath中是否存在对象的属性。要检查对象中深度嵌套的属性,可以使用点表示法或包含深度引用的keyPath的数组。

可选地,你可以提供一个值来检查它是否等于目标对象的keyPath中的值。此matcher使用“深度相等”(如toEqual()))并递归地检查所有字段的相等性。

下面的示例包含一个带有嵌套属性的houseForSale对象。我们使用tohave属性来检查对象中各种属性的存在性和值。

// Object containing house features to be tested
const houseForSale = {
  bath: true,
  bedrooms: 4,
  kitchen: {
    amenities: [‘oven‘, ‘stove‘, ‘washer‘],
    area: 20,
    wallColor: ‘white‘,
    ‘nice.oven‘: true,
  },
};

test(‘this house has my desired features‘, () => {
  // Simple Referencing
  expect(houseForSale).toHaveProperty(‘bath‘);
  expect(houseForSale).toHaveProperty(‘bedrooms‘, 4);

  expect(houseForSale).not.toHaveProperty(‘pool‘);

  // Deep referencing using dot notation
  expect(houseForSale).toHaveProperty(‘kitchen.area‘, 20);
  expect(houseForSale).toHaveProperty(‘kitchen.amenities‘, [
    ‘oven‘,
    ‘stove‘,
    ‘washer‘,
  ]);

  expect(houseForSale).not.toHaveProperty(‘kitchen.open‘);

  // Deep referencing using an array containing the keyPath
  expect(houseForSale).toHaveProperty([‘kitchen‘, ‘area‘], 20);
  expect(houseForSale).toHaveProperty(
    [‘kitchen‘, ‘amenities‘],
    [‘oven‘, ‘stove‘, ‘washer‘],
  );
  expect(houseForSale).toHaveProperty([‘kitchen‘, ‘amenities‘, 0], ‘oven‘);
  expect(houseForSale).toHaveProperty([‘kitchen‘, ‘nice.oven‘]);
  expect(houseForSale).not.toHaveProperty([‘kitchen‘, ‘open‘]);
});

写在最后

文中列出的只是其中一部分,更多详细信息请参考官网: https://facebook.github.io/jest/docs/en/expect.html#expectanything

系列教程:

   1. 前端测试框架Jest系列教程 -- Matchers(匹配器)

   2.前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)

   3.前端测试框架Jest系列教程 -- Mock Functions(模拟器)

   4.前端测试框架Jest系列教程 -- Global Functions(全局函数)

5.前端测试框架Jest系列教程 -- Expect(验证)

原文地址:https://www.cnblogs.com/Wolfmanlq/p/9119290.html

时间: 2024-11-05 20:33:49

前端测试框架Jest系列教程 -- Expect(验证)的相关文章

前端测试框架Jest系列教程 -- 匹配器

写在前面: 匹配器(Matchers)是Jest中非常重要的一个概念,它可以提供很多种方式来让你去验证你所测试的返回值,本文重点介绍几种常用的Matcher,其他的可以通过官网api文档查看. 常用的匹配方式: 第一种:相等匹配,这是我们最常用的匹配规则 test('two plus two is four', () => { expect(2 + 2).toBe(4); }); 在这段代码中 expact(2 + 2) 将返回我们期望的结果,通常情况下我们只需要调用expect就可以,括号中的

前端测试框架Jest系列教程 -- Matchers(匹配器)

写在前面: 匹配器(Matchers)是Jest中非常重要的一个概念,它可以提供很多种方式来让你去验证你所测试的返回值,本文重点介绍几种常用的Matcher,其他的可以通过官网api文档查看. 常用的匹配方式: 第一种:相等匹配,这是我们最常用的匹配规则 test('two plus two is four', () => { expect(2 + 2).toBe(4); }); 在这段代码中 expact(2 + 2) 将返回我们期望的结果,通常情况下我们只需要调用expect就可以,括号中的

前端测试框架Jest系列教程 -- Mock Functions

写在前面: 在写单元测试的时候有一个最重要的步骤就是Mock,我们通常会根据接口来Mock接口的实现,比如你要测试某个class中的某个方法,而这个方法又依赖了外部的一些接口的实现,从单元测试的角度来说我只关心我测试的方法的内部逻辑,我并不关注与当前class本身依赖的实现,所以我们通常会Mock掉依赖接口的返回,因为我们的测试重点在于特定的方法,所以在Jest中同样提供了Mock的功能,本节主要介绍Jest的Mock Function的功能. Jest中的Mock Function Mock

前端测试框架

一.为什么要进行测试? 一个 bug 被隐藏的时间越长,修复这个 bug 的代价就越大.大量的研究数据指出:最后才修改一个 bug 的代价是在 bug 产生时修改它的代价的10倍.所以要防患于未然. 从语言的角度讲 JavaScript 作为 web 端使用最广泛的编程语言,它是动态语言,缺乏静态类型检查,所以在代码编译期间,很难发现像变量名写错,调用不存在的方法, 赋值或传值的类型错误等错误. 例如下面的例子, 这种类型不符的情况在代码中非常容易发生 function foo(x) { ret

测试框架Mocha与断言expect

测试框架Mocha与断言expect在浏览器和Node环境都可以使用除了Mocha以外,类似的测试框架还有Jasmine.Karma.Tape等,也很值得学习. 整个项目源代码: 为什么学习测试代码?1. react的开发不适合网页端的调试和测试2. 把关所写代码质量,防止bug和漏洞 要测试的文件add.js测试文件命名为:add.test.js或者add.spec.js 测试脚本可以独立运行.测试脚本里包含一个或多个describe块,每个describe块应该包括一个或多个it块 add.

前端测试框架 puppeteer 文档翻译

puppeteer puppeteer 是一个通过DevTools 协议提供高级API 来控制 chrome,chromium 的 NODE库; puppeteer默认运行在 headless 模式, 也可配置后运行在全模式(non-headless). puppeteer可以做什么 大部分在浏览器里手动执行的动作都可以通过puppeteer实现! 这里有几个列子来让你开始. 生成页面截图和PDF. 爬取单页面应用生成提前渲染的内容(例如 SSR). 自动提交表单, UI测试, 键盘输入等. 创

前端测试框架学习

做了一年多的前端,从没有认真写过单元测试,对于常说的各种框架并不能彻底的分清,这次做了一个认真的学习与总结. 单元测试框架:Mocha, Jasmine等,因测试框架不包含断言库,因此需要引入断言库,Jasmine带有断言库assertions(未使用过).断言库 assert, shouldjs, chai等,具体的单元测试用例中使用karma是一款自动化测试工具,通过使用配置文件自动检测单元测试文件并进行测试,输出测试结果travis ci 持续集成服务,实现对代码库的代码的检测,编译,发布

ASP.NET MVC框架开发系列教程

本系列教程是自己在工作中使用到而记录的,如有错误之处,请给与指正 文章目录 MVC4 开篇 第一章 初识MVC4 第二章 下山遇虎(@helper) 第三章 Models模块属性详解 第四章 在MVC4.0中对脚本以及样式表的引用变化 第五章 MVC之Bundle详解

前端css框架SASS使用教程(转)

一.什么是SASS SASS是一种CSS的开发工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护. 本文总结了SASS的主要用法.我的目标是,有了这篇文章,日常的一般使用就不需要去看官方文档了. 二.安装和使用 2.1 安装 SASS是Ruby语言写的,但是两者的语法没有关系.不懂Ruby,照样使用.只是必须先安装Ruby,然后再安装SASS. 假定你已经安装好了Ruby,接着在命令行输入下面的命令: gem install sass 然后,就可以使用了. 2