mcous / vitest-when

Stub behaviors of Vitest mock functions based on how they are called
MIT License
28 stars 3 forks source link

`verifyAllMocksCalled` functionality #12

Closed buck06191 closed 5 months ago

buck06191 commented 5 months ago

What

In jest-when there's a feature we use at our company that verifies all of the when mocks are called by the end of a test -https://github.com/timkindberg/jest-when/blob/master/src/when.js#L216

In order for us to migrate to vitest it would be really good to have this added in to vitest-when.

Do you think this is something you'd consider adding in? We could look at raising a PR ourselves as a proposal if that's the case

mcous commented 5 months ago

Hi @buck06191! I'm opposed to verifyAllWhenMocksCalled on philosophical and technical grounds. See #10 for more details and background.

Philosophical

I think reliance on verifyAllWhenMocksCalled makes your test suite (and, by extension, your code under test) worse by tightly coupling your tests to your implementations and hiding useful design feedback from your tests:

Technical

Implementating verifyAllWhenMocksCalled requires adding global state to this library, which would then require cleanup. This is not something I'm willing to add to this library, since it would put restrictions on usage and introduce weird cleanup footguns

Userland

Not everyone has the same stubbing philosophy as me, and that's ok! While I am unwilling to implement this feature in vitest-when, I am interested in making it so that others, if they so chose, could implement this feature on top of vitest-when in their own suites

Personally, I think verifyAllMWhenMocksCalled is a poor testing practice, but I can see its usefulness as a test debugging tool. To that end, I've been working on #11 to add the ability to inspect individual stubs. I think the debug function should return enough information to determine if an individual stub has been called.

Are there any APIs you could imagine that would help implement a stub registry in your suite - that you would be responsible for managing cleanup of yourself - to give yourself the ability to loop through all your stubs to check them?

mcous commented 5 months ago

I think a fixture like this in your own codebase could work to replicate verifyAllWhenMocksCalled:

// when.ts
import { afterEach, expect } from 'vitest'
import { when as baseWhen } from 'vitest-when'

const mockRegistry = new Set()

export const when: typeof baseWhen = (spy, options) => {
  const result = baseWhen(spy, options)
  mockRegistry.add(spy)
  return result
}

afterEach(() => {
  const mocks = [...mockRegistry]
  mockRegistry.clear()

  for (const mock of mocks) {
    expect(mock).toHaveBeenCalled()
  }
})

Then, in your tests, import when from your fixture file instead of directly from vitest-when

mcous commented 5 months ago

@buck06191 thanks for taking the time to write this request! After giving it some additional thought over the weekend - and after landing #11 - I don't think this feature is a good fit for this library. I'd like to keep vitest-when pretty laser-focused on adding a conditional stubbing API to vi.fn(). I think call assertions and more frameworky features - like tracking the state of all mocks - are best left to userland and/or vitest itself.

Let me know if the above example is able to get you where you need to go, or if there are additional stub-level APIs that could be added to help you implement what you're trying to do