# Choosing the Right Matcher — Jest, Vitest & Testing Library

Source: https://www.skillbyai.com/en/javascript-testing/w-matchers

> toBe, toEqual, toThrow, toMatchObject and asymmetric matchers.

## Identity, structure and partial matches

`toBe` uses `Object.is`, so it suits primitives and checking that two references are the **same object**. `toEqual` compares **recursively by value** and ignores properties that are `undefined`; `toStrictEqual` also checks `undefined` properties, sparse arrays and class types. `toMatchObject` passes if the received object contains the expected subset. `toThrow` needs a **function** to call: `expect(() => fn()).toThrow('message')`. **Asymmetric matchers** like `expect.any(String)`, `expect.stringContaining`, `expect.objectContaining` and `expect.arrayContaining` can sit inside other expectations to ignore values you do not care about, such as generated ids or timestamps. Use `toBeCloseTo` for floating-point maths and `.not` to negate.

## Matchers side by side

Each line passes.

```typescript
const user = { id: 'u_81f2', name: 'Asha', roles: ['admin', 'editor'], createdAt: new Date() };

expect(2 + 2).toBe(4);
expect({ a: 1 }).toEqual({ a: 1 });            // same shape, different objects
expect({ a: 1 }).not.toBe({ a: 1 });           // ...but not the same reference
expect({ a: 1, b: undefined }).toEqual({ a: 1 });
expect({ a: 1, b: undefined }).not.toStrictEqual({ a: 1 });

expect(user).toMatchObject({ name: 'Asha' });   // subset is enough
expect(user).toEqual({
  id: expect.stringMatching(/^u_/),
  name: 'Asha',
  roles: expect.arrayContaining(['admin']),
  createdAt: expect.any(Date),
});

expect(0.1 + 0.2).toBeCloseTo(0.3);
expect(() => JSON.parse('{oops')).toThrow(SyntaxError);
```

## Assert on what matters, loosen the rest

Over-specific assertions (exact timestamps, full objects with dozens of fields) break on harmless changes. Pin the fields the behaviour depends on and use asymmetric matchers for the rest.

**Quiz:** Why does expect(fn()).toThrow() usually fail to catch the error?

- [ ] toThrow only works with async code
- [x] fn() runs and throws before expect receives anything; toThrow needs a function to call
- [ ] toThrow only accepts strings
- [ ] Errors are always swallowed by the runner

*Answer:* fn() runs and throws before expect receives anything; toThrow needs a function to call. Wrap the call: expect(() => fn()).toThrow().
