【问题标题】:How to force a return value of a function in jest?如何在玩笑中强制函数的返回值?
【发布时间】:2020-05-21 14:54:32
【问题描述】:

我正在尝试测试一些依赖于返回一些数据的 api 的代码。目前,我可以在这个函数中模拟 listNamespacedIngress 的调用:

async function getIngress(namespace) {
  try {
    const result = await k8sIngressApi.listNamespacedIngress(namespace, true);
    const resultSpec = result.body.items.filter(e => e.metadata.name === deploymentPrefix)[0];
    if (!resultSpec) {
      throw new TypeError('Ingress spec is undefined');
    }
    return resultSpec;
  } catch (e) {
    return Promise.reject(e);
  }
}

在这个库中使用 jest.mock 并模拟该函数的返回值,如下所示:

jest.mock('@kubernetes/client-node', () => ({
  KubeConfig: jest.fn().mockImplementation(() => ({
    loadFromCluster: jest.fn(),
    loadFromDefault: jest.fn(),
    makeApiClient: () => ({
      listNamespacedIngress: () =>
        Promise.resolve({
          body: {
            items: [
              {
                metadata: {
                  name: 'a',
                  namespace: 'b',
                },
                spec: {
                  rules: [
                    {
                      host: 'url.com',
                      http: {
                        paths: [
                          {
                            backend: {
                              serviceName: 'a',
                              servicePort: 80,
                            },
                          },
                        ],
                      },
                    },
                  ],
                },
              },

这样,如果 resultSpec 没有最终未定义,我可以测试初始返回值,如下所示(此测试通过):

  it('Should return storybook-staging ingress details', async () => {
    // When
    const result = await getIngress();

    // Then
    expect(result.metadata.name).toEqual('a');
  });

但是,我不确定如何强制 listNamespacedIngress 返回未定义?

编辑:添加了对模块的充分利用

const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const k8sDeploymentApi = kc.makeApiClient(k8s.AppsV1Api);
const k8sServiceApi = kc.makeApiClient(k8s.CoreV1Api);
const k8sIngressApi = kc.makeApiClient(k8s.NetworkingV1beta1Api);

const BRANCH_NAME = process.argv.slice(2)[0].toLowerCase();
const NAMESPACE = 'dev';
const deploymentPrefix = 'storybook-staging';

const DEPLOYMENT_CONFIG = getDeploymentConfig(deploymentPrefix, BRANCH_NAME);
const SERVICE_CONFIG = getServiceConfig(deploymentPrefix, BRANCH_NAME);
const INGRESS_CONFIG = getIngressConfig(deploymentPrefix);

const HTTP_CONFLICT = 409;

process.on('exit', code => {
  console.log(`About to exit with code: ${code}`);
});

async function getIngress(namespace) {
  try {
    const result = await k8sIngressApi.listNamespacedIngress(namespace, true);
    console.log(result);
    const resultSpec = result.body.items.filter(e => e.metadata.name === deploymentPrefix)[0];
    if (!resultSpec) {
      throw new TypeError('Ingress spec is undefined');
    }
    return resultSpec;
  } catch (e) {
    return Promise.reject(e);
  }
}

【问题讨论】:

    标签: javascript node.js unit-testing mocking jestjs


    【解决方案1】:

    listNamespacedIngress 更改返回值的正确方法是使其成为 Jest 间谍。由于它是嵌套的并且在测试中不容易被引用,所以它应该在模块模拟之外定义:

    // should be var because jest.mock is hoisted
    var mockListNamespacedIngress;
    
    jest.mock('@kubernetes/client-node', () => {
      mockListNamespacedIngress = jest.fn();
    
      return {
      KubeConfig: jest.fn().mockImplementation(() => ({
        loadFromCluster: jest.fn(),
        loadFromDefault: jest.fn(),
        makeApiClient: () => ({
          listNamespacedIngress: mockListNamespacedIngress
          ...
    

    这也允许断言该函数被调用:

    mockListNamespacedIngress.mockResolvedValue({ body: { items: [...] } });
    await expect(getIngress()).resolves.toEqual('a');
    expect(mockListNamespacedIngress).toBeCalledWith(...);
    
    ...
    
    mockListNamespacedIngress.mockResolvedValue({ body: { items: [] } });
    await expect(getIngress()).rejects.toThrow(TypeError);
    expect(mockListNamespacedIngress).toBeCalledWith(...);
    

    【讨论】:

    • 其实如果我可以再问一个问题!当我运行它时,我在尝试读取 body.items 等时变得未定义。当我控制台记录结果的值时,我得到这个 { [Function: mockConstructor] _isMockFunction: true, ... ... } 所以它似乎是返回模拟对象本身的值而不是我给它的值?编辑:我似乎也得到了 mockListNamespacedIngress 没有定义,尽管它位于文件的顶部并且在我的实现模拟中被引用
    • 如果在导入时立即调用 KubeConfig 可能会出现问题。请使用使用 KubeConfig 的模块更新问题。目前尚不清楚定义 getIngress 的模块与@kubernetes/client-node 之间的关系是什么。后者被嘲笑的方式可能取决于此。
    • 我明白了。如果 kc 在单独的模块中实例化,则可以模拟 KubeConfig 而不是整个 KubeConfig。但是由于它是在同一个模块中声明和使用的,因此将间谍公开为 mockListNamespacedIngress 变量来访问它仍然更容易。更新了帖子。
    • 我所知道的没有任何意义。您可以使用 ` () => { let mockListNamespacedIngress = jest.fn(); 暴露模拟模块上的间谍。 return { mockListNamespacedIngress, KubeConfig: ...` 并像import { mockListNamespacedIngress } from '@kubernetes/client-node' stackoverflow.com/questions/61843762/… 一样导入它,但使用虚拟导出似乎不适合一次性使用,如果将模拟存储在__mocks__ 中,这将是有意义的。
    • 顺便说一句 let 而不是 varjest.mock 应该会导致此错误 stackoverflow.com/questions/61843762 而不是使 mockListNamespacedIngress 静默未定义。我能想到的唯一原因是测试被转译为 ES5。由于 Jest 在 Node 中运行,这可能是不必要且有害的。如果 ES5 不可取,请检查 Jest 配置。
    猜你喜欢
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    • 2022-10-24
    • 2021-08-19
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 2021-10-16
    相关资源
    最近更新 更多