Search code examples
typescriptpiniavitest

Vitest TypeScript errors ts(2322) and ts(2339)


Background

I'm trying to make my first unit tests to my Vue app using Vitest (with emphasis on first, which is the reason I don't really understand what I'm doing yet), and the tests pass, but I get TypeScript linter errors and I don't understand how to fix them.

Test explanation: I'm testing a function that receives two Pinia stores, gets information from one store, and based on that information it decides whether to call a function of the other store.

First error - ts(2322) - "Type is missing the following properties"

Below is the relevant piece of code:

import { useStoreOne } from '@/stores/storeOne'
import { useStoreTwo } from '@/stores/storeTwo'
import { myFunction } from '@/functions'
import { describe, it, expect, vi, beforeEach } from 'vitest'

describe('myFunction', () => {
  let storeOne: ReturnType<typeof useStoreOne>
  let storeTwo: ReturnType<typeof useStoreTwo>

  beforeEach(() => {
    storeOne = {
      functionOne: vi.fn(),
      functionTwo: vi.fn(),
    }
    storeTwo = {
      functionThree: vi.fn(),
    }
  })

  // tests...
})

The error is on the assignments of storeOne and storeTwo inside the beforeEach. I understand that the problem is that I missed some properties, but those stores have many properties, and I think that declaring them all is not the solution that I'm looking for.

  • I tried using Partial<ReturnType<typeof useStoreOne>>, and it fixes the error, but it raises another error because myFunction gets the exact return type of useStoreOne, and not a partial return type.
  • I have noticed that placing as any after the assignment solves this problem, but... I don't wanna use any because that misses the whole point of using TypeScript.

Second error - ts(2339) - "Property 'mockReturnValue' does not exist"

The second error appears on the tests themselves, which are described below:

  it('should not call functionThree', async () => {
    storeOne.functionOne.mockReturnValue(false)
    await myFunction(storeOne, storeTwo)
    expect(storeTwo.functionThree).not.toHaveBeenCalled()
  })

  it('should call functionThree', async () => {
    storeOne.functionOne.mockReturnValue(true)
    storeTwo.functionTwo.mockReturnValue(69)
    await myFunction(storeOne, storeTwo)
    expect(storeTwo.functionThree).toHaveBeenCalledWith(69)
  })

Here, the error appears on the mockReturnValue calls. My linter recognizes the functions as... functions, but not as vi.fn(), i.e. they don't have properties such as mockReturnValue. Alongside with the first error, I believe that my mistake roots in the definitions of the stores in the beforeEach.

I tried all of changes mentioned above. I hereby remind that the tests pass, so I assume that it was properly written in its essence... but I leave some doubt that I should have written this completely different (e.g. I'm not even sure why I'm using beforeEach, and maybe that's the issue). I have also looked for answers on Stack Overflow, but I couldn't find answers to my issue. ChatGPT was totally out of tune, and couldn't handle the fact that I'm using Vitest and not Jest.


Solution

  • So, apparently I fell into the xy-problem: my issue wasn't what I thought my issue was.

    This is the correct code:

    import { setActivePinia, createPinia } from 'pinia'
    import { useStoreOne } from '@/stores/storeOne'
    import { useStoreTwo } from '@/stores/storeTwo'
    import { myFunction } from '@/functions'
    import { describe, it, expect, vi, beforeEach } from 'vitest'
    
    describe('myFunction', () => {
      let storeOne: ReturnType<typeof useStoreOne>
      let storeTwo: ReturnType<typeof useStoreTwo>
    
      beforeEach(() => {
        setActivePinia(createPinia())
        storeOne = useStoreOne()
        storeTwo = useStoreTwo()
      })
    
      it('should not call functionThree', async () => {
        vi.spyOn(storeOne, 'functionOne', 'get').mockReturnValueOnce(false)
        const functionThreeMock = vi.spyOn(storeTwo, 'functionThree')
        await myFunction(storeOne, storeTwo)
        expect(functionThreeMock).not.toHaveBeenCalled()
      })
    })
    

    I had to understand what spies are and how to use them, as well as setting an active Pinia and initialize my stores with it instead of creating objects that look like my Pinia stores.