同じモジュール内の別の関数をモックしたい
例
以下のような関数があった場合に
code:mod.ts
export const hoge = () => 'hoge'
export const fuga = () => hoge()
こんなテストコードを書いたとする
code:mod.test.ts
import { it, expect } from 'vitest'
import * as mod from './mod.ts'
it('hoge', () => {
expect(mod.hoge).toBe('hoge') // PASS
})
it('hoge -> fuga', () => {
vi.spyOn(mod, 'hoge').mockReturnValue('fuga')
expect(mod.hoge()).toBe('fuga') // PASS: 書き換えた関数自身はモックが効いている
})
it('fuga', () => {
vi.spyOn(mod, 'hoge').mockReturnValue('fuga')
expect(mod.fuga).toBe('fuga') // FAILED: expected fuga, but got hoge
})
しかしこれは fugaのテストケースに設定した spyOn は効果を発揮しないのでテストが通らない。
もし サンプルのように関数が同じモジュール内にあって、片方をモックすることでテストの挙動を変える必要があるのであれば どちらかを別のモジュールに切り出すのが一番楽(だとおもわれる)
解: モジュールを分ける方法
code: hoge.ts
export const hoge = () => 'hoge'
code:fuga.ts
import { hoge } from './hoge'
export const fuga = () => hoge()
code:hoge.test.ts
import { it, expect } from 'vitest'
import { hoge } from './hoge'
it('hoge', () => {
expect(hoge).toBe('hoge') // PASS
})
code:fuga.test.ts
import { it, expect } from 'vitest'
import * as modHoge from './hoge'
import { fuga } from './fuga'
it('fuga', () => {
vi.spyOn(modHoge, 'hoge').mockReturnValue('fua')
expect(fuga).toBe('fuga') // PASS
})
別解: クラスを使う方法
実装の集約を module 直下ではなく クラスにしておくと一応 spyOn が効くようになる。
無駄にクラスを作ると、状態を不意に持つ原因になるので現代ではあまりやらないほうがいいかもしれない(諸説あり)
code: mod.ts
export class Mod {
public static hoge() {
return 'hoge'
}
public static fuga() {
return this.hoge()
}
}
code:mod.test.ts
import { it, expect, vi } from 'vitest'
import { Mod } from './mod'
it('hoge', () => {
expect(Mod.hoge()).toBe('hoge')
})
it('fuga', () => {
vi.spyOn(Sample, 'hoge').mockReturnValue('fuga')
expect(Mod.fuga()).toBe('fuga')
})