【问题标题】:Mock static functions called in the main function that is being tested在正在测试的主函数中调用的模拟静态函数
【发布时间】:2021-03-30 06:47:12
【问题描述】:

我想在我正在测试的函数中模拟一些函数。

我有一个类,它有几个从所谓的mainFunction 调用的静态私有函数。我想特别测试MyClass.functionD 的结果(由mainFunction 调用,这是一个私有方法),因此,我想模拟MyClass.functionAMyClass.functionBMyClass.functionC 以返回默认结果,以便我的测试可以关注MyClass.fucntionD的结果。

export default class MyClass {

   static mainFunction(paramA: string, paramB: number): boolean {

        if (MyClass.functionA(paramA, paramB)) {
            return false;
        }

        if (!MyClass.functionB(paramA, paramB)) {
            return false;
        }

        if (MyClass.functionC(paramA, paramB)) {
            return false;
        }

        // I need to focus on the result of this function (i.e. private) for my test
        if (MyClass.functionD(paramA)) {
            return false;
        }

        return true;
   }

}

到目前为止,我已经尝试了 jest spyOn 和一些默认的模拟函数,但我迷路了,无法继续,因为我对 typescript / Javascript 真的很陌生。任何与我应该如何进行相关的提示/参考对我来说都足够了。 :) 谢谢。

【问题讨论】:

  • 最好将 mainFunction 视为一个单元,根本不考虑私有方法。情况并非总是如此,但如果你不能证明嘲笑是正当的,那么他们可能根本不应该被嘲笑。

标签: javascript typescript unit-testing jestjs mocking


【解决方案1】:

TypeScript 公共/私有关键字仅适用于 TypeScript 检查您的代码的方式 - 它们对 JavaScript 输出没有任何影响。所以你可以通过Bracket notation 访问这些私有方法,比如MyClass['fucntionA'],这将忽略TSC 的类型检查。然后你可以使用jest.spyOn来模拟这些私有方法。

这是我针对您的案例的测试策略:

MyClass.ts:

export default class MyClass {
  static mainFunction(paramA: string, paramB: number): boolean {
    if (MyClass.fucntionA(paramA, paramB)) {
      return false;
    }

    if (!MyClass.fucntionB(paramA, paramB)) {
      return false;
    }

    if (MyClass.fucntionC(paramA, paramB)) {
      return false;
    }

    if (MyClass.fucntionD(paramA)) {
      return false;
    }

    return true;
  }

  private static fucntionA(a, b) {
    return true;
  }

  private static fucntionB(a, b) {
    return false;
  }

  private static fucntionC(a, b) {
    return true;
  }
  private static fucntionD(a) {
    return false;
  }
}

MyClass.test.ts:

import MyClass from './MyClass';

describe('65376946', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should pass', () => {
    const fucntionASpy = jest.spyOn(MyClass as any, 'fucntionA').mockReturnValueOnce(false);
    const fucntionBSpy = jest.spyOn(MyClass as any, 'fucntionB').mockReturnValueOnce(true);
    const fucntionCSpy = jest.spyOn(MyClass as any, 'fucntionC').mockReturnValueOnce(false);
    const fucntionDSpy = jest.spyOn(MyClass as any, 'fucntionD').mockReturnValueOnce(true);

    const actual = MyClass.mainFunction('a', 1);
    expect(actual).toBeFalsy();
    expect(fucntionASpy).toBeCalledWith('a', 1);
    expect(fucntionBSpy).toBeCalledWith('a', 1);
    expect(fucntionCSpy).toBeCalledWith('a', 1);
    expect(fucntionDSpy).toBeCalledWith('a');
  });
});

单元测试结果:

PASS  examples/65376946/MyClass.test.ts
  65376946
    ✓ should pass (3 ms)

------------|---------|----------|---------|---------|-------------------
File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------|---------|----------|---------|---------|-------------------
All files   |   42.86 |       50 |      20 |   42.86 |                   
 MyClass.ts |   42.86 |       50 |      20 |   42.86 | 4,8,12,19-34      
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.168 s

【讨论】:

  • 完美!但我有个问题。你能解释一下使用toBeCalledWith的目的吗?我们是否可以忽略它,因为我们已经将参数传递给 mainFunction 最终将在子函数中使用(在这种情况下,它们无论如何都会被模拟)?
  • @ihaider 断言要使用预期参数调用模拟函数。换句话说,目的是断言你的代码执行路径是正确的。每个测试用例都应该测试一个代码执行路径
猜你喜欢
  • 1970-01-01
  • 2019-01-18
  • 2018-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-02
  • 2015-11-13
  • 1970-01-01
相关资源
最近更新 更多