测试一个函数 returns 一个带有函数的对象 - JavaScript (Jest)

Testing a function that returns an object with a function - JavaScript (Jest)

我遇到了一个关于函数单元测试的问题。有一个函数 SOP3loginConfig 其中 returns 一个对象,在那个对象中我们有一个函数 isSOP3 那个 returns 一个布尔值,我想测试这个函数并覆盖测试部分

函数的实际实现sop3login.ts

export const SOP3loginConfig = (props: IVariables) => {
  const { i18n } = props;
  return {
    buttonLabel: props.user != null ? i18n('StartJourney') : i18n('logIn'),
    loginLink:"/login?redirectUrl="+window.location.href,
    isSOP3: async() => {
      let userData = await ADCServices.getUserInfo();
      if (!userData.session.tammUserInfo || userData.session.tammUserInfo.Type!="SOP3") {
        props.SOP3toggleModal(props,true);
        setTimeout(()=>props.SOP3toggleModal(props,false),5000)
        return false;
      } else {
        return true;
      }
    },
  };
};

我的积分work.ts

import { SOP3loginConfig } from 'client/.../sop3login';

const start = async (props: IVariables) => {
  if (props.user) {
    if (await SOP3loginConfig(props).isSOP3()) {
      props.history.push('/adc/card-renewal/customs');
    }
  } else {
    props.history.push(SOP3loginConfig(props).loginLink);
  }
};

我的单元测试实现work.test.ts

  describe('Start SOP3loginConfig should be called', () => {
    it('Should call the SOP3', async () => {
      props.user = true;
      // const isSOP3Mock = () => {
      //   return true;
      // };
      // let SOP3loginConfig = async (props: any) => {
      //   return true;
      //   // return {
      //   //   isSOP3: isSOP3Mock,
      //   // };
      // };
      let SOP3loginConfig = jest.fn(props => {
        return {
          isSOP3: jest.fn(() => {
            return true;
          }),
        };
      });
      functions.start(props);
      expect(await SOP3loginConfig(props).isSOP3).toHaveBeenCalled();
      expect(props.history.push).toHaveBeenCalled();
    });
  });

我收到错误

    expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

       97 |       });
       98 |       functions.start(props);
    >  99 |       expect(await SOP3loginConfig(props).isSOP3).toHaveBeenCalled();
          |                                                   ^
      100 |       expect(props.history.push).toHaveBeenCalled();
      101 |     });
      102 |   });

All I want to is cover the if (await SOP3loginConfig(props).isSOP3()) part in work.ts.

单元测试解决方案如下:

sop3login.ts:

import ADCServices from './adc.service';

export interface IVariables {
  user: any;
  history: IHistory;
  i18n(name: string): any;
  SOP3toggleModal(props: IVariables, flag: boolean): void;
}

interface IHistory {
  push(router: string): any;
}

export const SOP3loginConfig = (props: IVariables) => {
  const { i18n } = props;
  return {
    buttonLabel: props.user != null ? i18n('StartJourney') : i18n('logIn'),
    loginLink: '/login?redirectUrl=' + window.location.href,
    isSOP3: async () => {
      const userData = await ADCServices.getUserInfo();
      if (!userData.session.tammUserInfo || userData.session.tammUserInfo.Type !== 'SOP3') {
        props.SOP3toggleModal(props, true);
        setTimeout(() => props.SOP3toggleModal(props, false), 5000);
        return false;
      } else {
        return true;
      }
    },
  };
};

adc.service.ts:

export default class ADCServices {
  public static async getUserInfo() {
    return {
      session: {
        tammUserInfo: {
          Type: 'real type',
        },
      },
    };
  }
}

work.ts:

import { SOP3loginConfig, IVariables } from './sop3login';

const start = async (props: IVariables) => {
  if (props.user) {
    if (await SOP3loginConfig(props).isSOP3()) {
      props.history.push('/adc/card-renewal/customs');
    }
  } else {
    props.history.push(SOP3loginConfig(props).loginLink);
  }
};

export { start };

work.test.ts:

import { start } from './work';
import { SOP3loginConfig, IVariables } from './sop3login';

jest.mock('./sop3login.ts', () => {
  const mObj = {
    isSOP3: jest.fn(),
  };
  return { SOP3loginConfig: jest.fn(() => mObj) };
});

describe('Start SOP3loginConfig should be called', () => {
  it('Should call the SOP3', async () => {
    const mProps: IVariables = {
      user: true,
      history: { push: jest.fn() },
      i18n: jest.fn(),
      SOP3toggleModal: jest.fn(),
    };
    await start(mProps);
    expect(SOP3loginConfig).toBeCalledWith(mProps);
    expect(SOP3loginConfig(mProps).isSOP3).toBeCalledTimes(1);
  });
});

包含覆盖率报告的单元测试结果:

 PASS  src/Whosebug/59627009/work.test.ts
  Start SOP3loginConfig should be called
    ✓ Should call the SOP3 (7ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |    77.78 |       50 |      100 |    71.43 |                   |
 work.ts  |    77.78 |       50 |      100 |    71.43 |               6,9 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.078s, estimated 10s

源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/Whosebug/59627009