Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Include toBeCloseTo in Jest .toMatchObject

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.

like image 370
Dan Dascalescu Avatar asked Nov 19 '18 06:11

Dan Dascalescu


People also ask

Which assertion used to test a value is with exact equality in jest?

Common Matchers​ The simplest way to test a value is with exact equality. expect(2 + 2).toBe(4); }); In this code, expect(2 + 2) returns an "expectation" object.

What is expect assertions in jest?

expect. assertions(number) will verify that a certain number of assertions are called during a test. Often, this is useful when testing asynchronous code, so as to make sure that assertions in a callback actually got called.

What is the difference between toBe and toEqual?

toEqual and the . toBe are equivalent, because the first thing they both check is if the values are strictly equal. So performance wise there is no difference. . toEqual just handles more cases if the strict equality fails on non-primatives.

Which matcher function is tested greater than condition?

The toBeGreaterThan and toBeLessThan matchers check if something is greater than or less than something else.


1 Answers

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
  });
});
like image 99
Brian Adams Avatar answered Nov 14 '22 09:11

Brian Adams