Skip to content
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

Support td.replace for Vitest #484

Closed
nullpaper opened this issue Apr 27, 2022 · 6 comments
Closed

Support td.replace for Vitest #484

nullpaper opened this issue Apr 27, 2022 · 6 comments

Comments

@nullpaper
Copy link

How can we get Testdouble replace working with Vitest https://vitest.dev

Vitest and td almost work together smoothly. td.replace works fine when you're mocking a local object, but it doesn't work on an imported module:

import * as td from 'test-double'

td.replace(Math, 'random', () => 1234)
Math.random()
// -> 1234 🎉

td.replace('some-module')
// -> Nope. Nothing replaced.

As Vitest is a 'drop-in' replacement for Jest, I tried using testdouble-jest but of course that doesn't work because Vitest is modules-based and Jest is commonjs so you end up with conflicts on requireMock rather than importMock.

TypeError: Cannot set properties of undefined (setting 'requireMock')
> module.exports node_modules/testdouble-jest/index.js:25:23

But even tweaking these makes no difference as the import doesn't seem to get swapped out. I'm guessing this is a modules vs CJS issue somewhere in the deep. I'm hoping someone with more experience can point in the right direction.

Environment

  • testdouble 3.16.2
  • testdouble-jest 2.0.0
@rosston
Copy link
Member

rosston commented Apr 27, 2022

I've never heard of vitest before today, but FWIW, it says it includes tinyspy, which says in its readme:

Warning! Does not support ESM mocking. You can use tinyspy with vitest, who performs additional transformations to make ESM mocking work.

And from a quick look at the vitest docs, it looks like they have a mock method that seems similar to Jest's.

That error doesn't look like what I'd expect to see for a failure for ESM imports given the source of testdouble-jest. @nullpaper Can you share how you're calling the testdouble-jest setup function as shown in the installation section?

@searls
Copy link
Member

searls commented Apr 28, 2022

I don't know what vitest is, but what's test-double?

import * as td from 'test-double'

@mcous
Copy link
Contributor

mcous commented May 1, 2022

I've been using testdouble for a long time and was also recently introduced to vitest. After noodling for a bit, I'm finding this to be a pretty serviceable integration of the two tools for my own needs:

import {vi, describe, it, afterEach} from 'vitest'
import * as td from 'testdouble'

import {validateOptions} from '../some-dependency'
import {calculate} from '../some-other-dependency'

import {doTheThing as subject} from '../some-code-under-test'

vi.mock('../some-dependency', () => td.object())
vi.mock('../some-other-dependency', () => td.object())

describe('some test subject', () => {
  afterEach(() => {
    td.reset()
  })

  it('should ensure options before creating something', () => {
    const inputValue = 1234
    const inputOptions = {"hello": "from"}
    const validatedOptions = {"the other": "side"}
    const expectedResult = 6789

    td.when(validateOptions(inputOptions)).thenReturn(validatedOptions)
    td.when(calculate(inputValue, validatedOptions)).thenReturn(expectedResult)

    const result = subject(inputValue, inputOptions)
    expect(result).to.equal(expectedResult)
  })
})

@nullpaper
Copy link
Author

@mcous yeahhh this gets me 95% of the way, thanks.

@mcous
Copy link
Contributor

mcous commented Nov 22, 2022

@nullpaper I've continued to use testdouble together with vitest for a few months, and while the vi.mock('./module-name', () => td.object()) method was ok for a while, I ended up chafing at the lack of actual module imitation.

I've just published testdouble-vitest to fill that particular gap by creating versions of td.replaceEsm and td.reset that work with vitest's module/mocking system:

import { beforeEach, afterEach, describe, it, expect } from 'vitest'
import { replaceEsm, reset } from 'testdouble-vitest'
import * as td from 'testdouble'

describe('getting a report', () => {
  let dataLoader: typeof import('./load-report-data')
  let reportGenerator: typeof import('./generate-report')
  let subject: typeof import('./get-report')

  beforeEach(async () => {
    dataLoader = await replaceEsm('./load-report-data')
    reportGenerator = await replaceEsm('./generate-report')
    subject = await import('./get-report')
  })

  afterEach(() => {
    reset()
  })

  it('should load the data and generate the report', async () => {
    const reportData = { id: 'abc123' }
    const report = { id: 'abc123', summary: 'hello world' }

    td.when(dataLoader.loadReportData('abc123')).thenResolve(reportData)
    td.when(reportGenerator.generateReport(reportData)).thenReturn(report)

    const result = await subject.getReport('abc123')

    expect(result).to.eql(report)
  })
})

If you're still using vitest and testdouble together, I'd be curious to hear if this works for you!

@searls
Copy link
Member

searls commented Nov 22, 2022

This is wonderful, @mcous!

If you can take the time to document this in a PR on this file (and anywhere else that mentioning it makes sense) I'd be happy to promote your package!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants