How to Use Property Matchers on Dynamic Values in Jest Tests

Learn about property matchers in Jest

Jest, my all-time favorite JS testing framework, has a useful feature for ensuring an object matches an expected snapshot. As mentioned in their docs:

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.

But this feature can go beyond just UIs. I have a use case where I want to ensure the shape of objects in json. However, since I have dynamic values in these objects, I looked to utilize the property matcher feature in Jest.

For example, I could have a test scenario where creating a student returns the student with the latest ID. My first time running this test, it would pass, but the next it wouldn’t because the ID has incremented. Likewise, the snapshot won’t match running a subsequent time.

it('verify response shape', () => {
    const createdStudent = api.createStudent({name: 'Kelly Jones'});
    expect(createdStudent).toBe({
        id: '23',
        name: 'Kelly Jones'
    });
    // example with snapshotexpect(createdStudent).toMatchSnapshot();
});

This is where property matchers come in and assist with the expect.any(T) function, where T is a Javascript type. Here is the same test which is more resilient:

it('verify response shape', () => {
    const createdStudent = api.createStudent({name: 'Kelly Jones'});
    expect(createdStudent).toMatchSnapshot({
        id: expect.any(String),
        name: 'Kelly Jones'
    });
});

For something more complex, such as a paginated list of students:

const pageResultMatch = {
    id: expect.any(String),
    name: expect.any(String)
  };
const pageMatcher = {
  data: [
    {...pageResultMatch},
    {...pageResultMatch},
    {...pageResultMatch},
    {...pageResultMatch},
    {...pageResultMatch}
  ],
  page: {
    number: 0,
    size: 5,
    totalElements: expect.any(Number),
    totalPages: expect.any(Number),
  },
};

it('returns correct result size and page structure', () => {
    const result = api.getStudents({size: 5});
    expect(result).toMatchSnapshot(pageMatcher);
})

Unfortunately, the property matcher does not apply to all entries in an array. To ignore all values in the objects of entries, then using expect.any(Array) will simply expect an array value.