Skip to content

Utility to automock class instance #4001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
4 tasks done
everett1992 opened this issue Aug 22, 2023 · 8 comments · May be fixed by #7761
Open
4 tasks done

Utility to automock class instance #4001

everett1992 opened this issue Aug 22, 2023 · 8 comments · May be fixed by #7761
Labels
p2-nice-to-have Not breaking anything but nice to have (priority)

Comments

@everett1992
Copy link
Contributor

Clear and concise description of the problem

As a developer using vitest I want an easy way to create mock instances of a class. While I can us vi.mock('module') to automock the entire file I have to jump thru hoops to get a mock instance, mainly to avoid TypeScript errors

import { Class } from 'module'
vi.mock('module')
// @ts-ignore-error Ignore constructor argument errors
const mockInstance  = new Class()

vi.mock will mock the whole module, so if you wanted another exported value to use the real implementation you need a factory to import the real implementation.

Suggested solution

import { Class } from 'module'
import { vi } from 'vitest'

const mock = vi.mockInstance(Class)

Alternative

No response

Additional context

No response

Validations

@sheremet-va sheremet-va added the enhancement New feature or request label Aug 23, 2023
@splanard
Copy link

splanard commented Apr 17, 2024

I would be very interested in this feature !!

In my particular case, I'm using Vitest to unit test Vue.js components. And I often need to instanciate a class to provide a mock to components that use inject. At the moment, I'm forced to use workarounds like the one suggested by @everett1992.

It would also be very useful to be able to retrieve the mocked methods of this mocked class instance, to specify a behaviour if needed, depending on the test case.

import { Class } from 'module'
vi.mock('module')
// @ts-ignore-error Ignore constructor argument errors
const mockInstance  = new Class()

mockInstance.someMethod.mockReturnValue('mocked value');
expect(mockInstance.someMethod).toHaveBeenCalled();

@hi-ogawa
Copy link
Contributor

Is this already possible with vi.importMock? This should return "automock"-ed module without actually setting up module mocking. I made a simple example here:

https://stackblitz.com/edit/vitest-dev-vitest-hnt4sl?file=src%2Fsome.test.ts

//
// some.test.ts
//
import { vi, test, expect } from 'vitest';

test('repro', async () => {
  const { SomeClass } = await vi.importMock<typeof import('./some-class')>('./some-class');

  const someInstance = new SomeClass();

  // type check works
  vi.mocked(someInstance.someMethod).mockReturnValue('hello');
  expect(someInstance.someMethod(1234)).toMatchInlineSnapshot(`"hello"`);
});


//
// some-class.ts
//
export class SomeClass {
  someMethod(x: number) {
    return String(x);
  }
}

@everett1992
Copy link
Contributor Author

everett1992 commented Apr 18, 2024 via email

@hi-ogawa
Copy link
Contributor

hi-ogawa commented Apr 18, 2024

I see. Your concern was the type error on construction. Then I'm not sure if it's really important features to just help silencing type errors.
Do you see more on this feature other than type errors?

EDIT: Well, I actually thought it would be a neat feature if we can expose mockObject utility for users to easily simulate automock without concerned with modules.

public mockObject(object: Record<Key, any>, mockExports: Record<Key, any> = {}) {

@splanard
Copy link

splanard commented Apr 18, 2024

Is this already possible with vi.importMock? This should return "automock"-ed module without actually setting up module mocking. I made a simple example here:

https://stackblitz.com/edit/vitest-dev-vitest-hnt4sl?file=src%2Fsome.test.ts

//
// some.test.ts
//
import { vi, test, expect } from 'vitest';

test('repro', async () => {
  const { SomeClass } = await vi.importMock<typeof import('./some-class')>('./some-class');

  const someInstance = new SomeClass();

  // type check works
  vi.mocked(someInstance.someMethod).mockReturnValue('hello');
  expect(someInstance.someMethod(1234)).toMatchInlineSnapshot(`"hello"`);
});


//
// some-class.ts
//
export class SomeClass {
  someMethod(x: number) {
    return String(x);
  }
}

I get this error while trying to instanciate the class your way :

This expression is not constructable.
  Type 'Promise<MockedObjectDeep<typeof import("...")>>' has no construct signatures.ts(2351)

The class is the only and default export of the module (single file class).

@splanard
Copy link

splanard commented Apr 18, 2024

Ok, it works this way :

//
// some.test.ts
//
import { vi, test, expect } from 'vitest';

test('repro', async () => {
  const SomeClass = (await vi.importMock<typeof import('./SomeClass')>('./SomeClass')).default;

  const someInstance = new SomeClass();

  // type check works
  vi.mocked(someInstance.someMethod).mockReturnValue('hello');
  expect(someInstance.someMethod(1234)).toMatchInlineSnapshot(`"hello"`);
});


//
// SomeClass.ts
//
export default class SomeClass {
  someMethod(x: number) {
    return String(x);
  }
}

Thx for the tip @hi-ogawa !!

@superveetz-arb
Copy link

I see. Your concern was the type error on construction. Then I'm not sure if it's really important features to just help silencing type errors. Do you see more on this feature other than type errors?

EDIT: Well, I actually thought it would be a neat feature if we can expose mockObject utility for users to easily simulate automock without concerned with modules.

public mockObject(object: Record<Key, any>, mockExports: Record<Key, any> = {}) {

👍

@sheremet-va sheremet-va added enhancement: pending triage and removed enhancement New feature or request labels Oct 29, 2024
@sheremet-va sheremet-va moved this to P2 - 2 in Team Board Oct 29, 2024
@sheremet-va sheremet-va added the p2-to-be-discussed Enhancement under consideration (priority) label Oct 29, 2024
@sheremet-va
Copy link
Member

We can expose something like this:

const SomeClass = vi.mockObject(OriginalClass) 
const instance = new SomeClass() // Mock<OriginalClass>
expect(SomeClass).toHaveBeenCalled()

@sheremet-va sheremet-va moved this from P2 - 2 to Approved in Team Board Mar 28, 2025
@sheremet-va sheremet-va added p2-nice-to-have Not breaking anything but nice to have (priority) and removed p2-to-be-discussed Enhancement under consideration (priority) enhancement: pending triage labels Mar 28, 2025
@hi-ogawa hi-ogawa linked a pull request Mar 29, 2025 that will close this issue
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p2-nice-to-have Not breaking anything but nice to have (priority)
Projects
Status: Approved
Development

Successfully merging a pull request may close this issue.

5 participants