Include toBeCloseTo in Jest .toMatchObject

2020-07-10 06:45发布

I'm testing that an object matches a set of fields, but one of them is floating point and I need to use .toBeCloseTo. How can that be done within one expect?

expect(foo).toMatchObject({
  bar: 'baz',
  value: ???.toBeCloseTo(5),  // TODO
});

I could use expect(foo.value).toBeCloseTo(5), but I don't want to break the logic into multiple expects, one for each floating point number.

1条回答
欢心
2楼-- · 2020-07-10 07:09

Issue

The docs for toMatchObject states "You can match properties against values or against matchers".

Unfortunately, toBeCloseTo is not currently available as an asymmetric matcher, it looks like these are the only asymmetric matchers currently provided by Jest.


Solution

If you are using Jest v23 or higher you can create your own, essentially duplicating toBeCloseTo using expect.extend:

expect.extend({
  toBeAround(actual, expected, precision = 2) {
    const pass = Math.abs(expected - actual) < Math.pow(10, -precision) / 2;
    if (pass) {
      return {
        message: () => `expected ${actual} not to be around ${expected}`,
        pass: true
      };
    } else {
      return {
        message: () => `expected ${actual} to be around ${expected}`,
        pass: false
      }
    }
  }
});

const foo = {
  bar: 'baz',
  value: 4.9999
};

test('foo', () => {
  expect(foo.value).toBeAround(5, 3);  // SUCCESS in Jest > v20
  expect(foo).toMatchObject({
    bar: 'baz',
    value: expect.toBeAround(5, 3)  // SUCCESS only in Jest > v23
  });
});

Note that expect.extend creates a matcher that can be used within functions like toMatchObject only in Jest v23 and higher.


Alternate Solution

From this post by a Jest collaborator: "Although it is implied but not currently documented, Jest assertions evaluate asymmetric matcher objects as defined in Jasmine".

An asymmetric matcher using the logic from toBeCloseTo can be created like this:

const closeTo = (expected, precision = 2) => ({
  asymmetricMatch: (actual) => Math.abs(expected - actual) < Math.pow(10, -precision) / 2
});

const foo = {
  bar: 'baz',
  value: 4.9999
};

test('foo', () => {
  expect(foo).toMatchObject({
    bar: 'baz',
    value: closeTo(5, 3)  // SUCCESS
  });
});
查看更多
登录 后发表回答