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

发表于:2018-6-01 09:47

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:KenWang    来源:博客园

  写在前面
  在编写测试时,我们通常需要检查值是否满足某些条件,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),
);
});
});

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号