vitest-dev / vitest

Next generation testing framework powered by Vite.
https://vitest.dev
MIT License
12.94k stars 1.16k forks source link

spyOn does not preserve static properties on functions #6765

Open akellbl4 opened 3 hours ago

akellbl4 commented 3 hours ago

Describe the bug

When trying to spy on a function that has static properties, the spy function won't inherit the properties. I couldn't find any way to add them to create a spy from the original function.

Reproduction

Abstract example https://stackblitz.com/edit/vitest-dev-vitest-uvuhzq?file=test%2Fmain.test.ts

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 18.20.3 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.2.3 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    @vitest/ui: latest => 2.1.3 
    vite: latest => 5.4.9 
    vitest: latest => 2.1.3

Used Package Manager

npm

Validations

hi-ogawa commented 2 hours ago

It looks like your repro is basically https://vitest.dev/guide/mocking.html#mocking-pitfalls so it's working expected. Can you elaborate what you mean by "static properties on functions"?

github-actions[bot] commented 2 hours ago

Hello @akellbl4. Please provide a minimal reproduction using a GitHub repository or StackBlitz (you can also use examples). Issues marked with needs reproduction will be closed if they have no activity within 3 days.

akellbl4 commented 2 hours ago

@hi-ogawa sorry for the incorrect repro. I've simplified my example.

The main idea is if I define a function assign property to it and then spy on it the property won't be present because the spy wrapper doesn't have it and doesn't inherit it from the original function.

Posting it right here as well:

import { vi, expect, test } from 'vitest';

interface Fn {
  (): void;
  flag: boolean;
}

export const fn: Fn = () => {
  console.log('Boom');
};

fn.flag = true;

const methods = {
  fn,
};

test('run', () => {
  vi.spyOn(methods, 'fn');
  expect(methods.fn.flag).toBe(true);
});
akellbl4 commented 2 hours ago

A common case for this is something like createAction from @redux/toolkit, which adds type as a property on the action creator.

So if it's spied on, the type wouldn't be matched in a reducer.

import { createAction } from '@redux/toolkit'

const updateAction = createAction('a')

console.log(updateAction.type) // -> a

function reducer(state, action) {
  switch (action.type) {
    case updateAction.type: { // .type is `undefined` when `updateAction` is spied on
      return state
    }
    default: return state
  }
}