Skip to content

expect(result).toEqual(expect.objectContaining(...)) broken in V4 when result is a Proxy. #9488

@dwjohnston

Description

@dwjohnston

Describe the bug

Here is a regular test testing against a proxy:

  // V3 ✅
  // V4 ✅
  it("v1", () => {
    const target = {
      page: 2,
      pageSize: 30,
      foo: "bar",
    };

    const handler = {
      get(target, prop) {
        return target[prop];
      },
    };

    const proxy = new Proxy(target, handler);
    expect(proxy).toEqual(
      expect.objectContaining({
        page: 2,
        foo: "bar",
      }),
    );
  });

This passes in both V3 and V4.


If we change our handler implementation to:

    const handler = {
      get(target, prop) {
        return "world";
      },
    };

ie. all fields will return 'world' no matter what, then we get apparently broken behaviour.


V4 erroneously passes the test. It appears to ignore the proxy implementation.

  // This test should be failing.
  // V3 ❌ (correct)
  // V4 ✅ (incorrect)
  it("v2", () => {
    const target = {
      page: 2,
      pageSize: 30,
      foo: "bar",
    };

    const handler = {
      get(target, prop) {
        return "world";
      },
    };

    const proxy = new Proxy(target, handler);
    expect(proxy).toEqual(
      expect.objectContaining({
        page: 2,
        foo: "bar",
      }),
    );
  });

V4 erroneously fails the test

  // This test should be passing.
  // V3 ✅ (correct)
  // V4 ❌ (incorrect)
  it("v2 - inverse", () => {
    const target = {
      page: 2,
      pageSize: 30,
      foo: "bar",
    };

    const handler = {
      get(target, prop) {
        return "world";
      },
    };

    const proxy = new Proxy(target, handler);
    expect(proxy).toEqual(
      expect.objectContaining({
        page: "world",
        foo: "world",
      }),
    );
  });
 FAIL  src/objectContaining.test.ts > proxy > v2 - inverse
AssertionError: expected { page: 'world', …(2) } to deeply equal ObjectContaining{…}

- Expected
+ Received

- ObjectContaining {
+ {
    "foo": "world",
    "page": "world",
+   "pageSize": "world",
  }

 ❯ src/objectContaining.test.ts:116:19
    114| 
    115|     const proxy = new Proxy(target, handler);
    116|     expect(proxy).toEqual(
       |                   ^
    117|       expect.objectContaining({
    118|         page: "world",

It seems to be tripping up on the extra property pageSize that is not in the objectContaining.

Note that it doesn't have a problem with the values of foo and page.


V4 erroneously fails the test 2

  // This test should be passing.
  // V3 ✅ (correct)
  // V4 ❌ (incorrect)
  it("v2 - inverse 2", () => {
    const target = {
      page: 2,
      pageSize: 30,
      foo: "bar",
    };

    const handler = {
      get(target, prop) {
        return "world";
      },
    };

    const proxy = new Proxy(target, handler);
    expect(proxy).toEqual(
      expect.objectContaining({
        page: "world",
        foo: "world",
        pageSize: "world",
      }),
    );
  });
});
 FAIL  src/objectContaining.test.ts > proxy > v2 - inverse 2
AssertionError: expected { page: 'world', …(2) } to deeply equal ObjectContaining{…}

- Expected
+ Received

- ObjectContaining {
+ {
    "foo": "world",
    "page": "world",
    "pageSize": "world",
  }

 ❯ src/objectContaining.test.ts:141:19
    139| 
    140|     const proxy = new Proxy(target, handler);
    141|     expect(proxy).toEqual(
       |                   ^
    142|       expect.objectContaining({
    143|         page: "world",

This further demonstrates that it's not the values that it is apparently tripping up on - but there is some equality checking fail here.


Reproduction

https://github.com/dwjohnston/vitest-broken-proxies

Other notes:

I initially discovered this migrating a vue project from vitest 3 to 4.

A vue test like this:

  it("v2", () => {
    const p2 = reactive({
      page: computed(() => 2),
      pageSize: computed(() => 30),
      foo: computed(() => "bar"),
    });

    expect(p2).toEqual(
      expect.objectContaining({
        page: 2,
        foo: "bar",
      }),
    );
  });

Will fail in the same manner.

System Info

System:
    OS: macOS 15.6.1
    CPU: (12) arm64 Apple M4 Pro
    Memory: 97.77 MB / 24.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.21.1 - /Users/davidjohnston/.nvm/versions/node/v22.21.1/bin/node
    npm: 11.6.2 - /Users/davidjohnston/.nvm/versions/node/v22.21.1/bin/npm
    pnpm: 10.16.1 - /Users/davidjohnston/Library/pnpm/pnpm
    bun: 1.2.21 - /Users/davidjohnston/.bun/bin/bun
  Browsers:
    Brave Browser: 143.1.85.120
    Chrome: 144.0.7559.59
    Edge: 144.0.3719.82
    Firefox: 147.0.1
    Safari: 18.6
  npmPackages:
    vitest: 4.0.17 => 4.0.17

Used Package Manager

npm

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    p3-minor-bugAn edge case that only affects very specific usage (priority)pending triage

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions