首页 > 解决方案 > 如何使用 jest 在 javascript 中测试抽象函数?

问题描述

我有 2 个 javascript A 和 B 函数定义如下,并通过另一个名为 API 的函数公开,如下所示,我想测试函数 A 以查看它是否正在调用 B。

function A () {
  B()
}

function B () {
  console.log('B is called')
}

export function API (){
  return {
    a: A,
    b: B
  }
}

我尝试测试功能的方式如下,它们不起作用。

import { API } from './actions-beta'

describe('test A', () => {
  test('', () => {
    const fn = API()
    console.log(fn)
    const spy = jest.spyOn(fn, 'b')
    fn.a()
    expect(spy).toHaveBeenCalled()
  })
})

标签: javascriptunit-testingjestjs

解决方案


没有办法测试是否B调用了代码的编写方式。

细节

jest.spyOn 用 spy 替换对象上的函数属性

jest.spyOn(fn, 'b')将用间谍替换b对象上的属性。fn

fn.a()直接调用A的调用B,它不调用fn.b,因此永远不会调用间谍。

解决方案

A需要B 使用可以在测试期间替换为 spy 的对象属性进行调用。

创建间谍时,对象通常是模块

这就是为什么监视导出函数很容易而监视非导出函数非常困难的原因,这带来了一个重要的点:如果一个函数在同一个模块中调用一个非导出函数,那么它只是一个实现细节在模块外部不可见,并且使用黑盒测试方法不需要对其进行测试。

如果您发现这B不仅仅是一个实现细节,并且您想窥探或存根其功能,那么最简单的方法(特别是对于此代码,其中导出是一个每次调用时都会创建一个新对象的函数)是移动B到它的自己的模块:


动作-beta.js

import { B } from './lib';

export function A () {
  B()
}

export function API (){
  return {
    a: A,
    b: B
  }
}

lib.js

export function B () {
  console.log('B is called')
}

考试:

import { API } from './actions-beta'
import * as lib from './lib';   // import the module with B

describe('test A', () => {
  test('', () => {
    const fn = API()
    console.log(fn)
    const spy = jest.spyOn(lib, 'B')   // spy on B using its module
    fn.a()
    expect(spy).toHaveBeenCalled()   // SUCCESS
  })
})

推荐阅读