在Jest中模拟静态方法

78

我在jest中模拟静态方法时遇到了麻烦。假设你有一个带有静态方法的类A:

export default class A {
  f() {
    return 'a.f()'
  }

  static staticF () {
    return 'A.staticF()'
  }
}

还有一个B类,它导入了A

import A from './a'

export default class B {
  g() {
    const a = new A()
    return a.f()  
  }

  gCallsStaticF() {
    return A.staticF()
  }
}

现在您想要模拟 A。模拟 f() 很容易:

import A from '../src/a'
import B from '../src/b'

jest.mock('../src/a', () => {
  return jest.fn().mockImplementation(() => {
    return { f: () => { return 'mockedA.f()'} }
  })
})

describe('Wallet', () => {
  it('should work', () => {
    const b = new B()
    const result = b.g()
    console.log(result) // prints 'mockedA.f()'
  })
})

但是,我找不到任何关于如何模拟 A.staticF 的文档。这是否可能?

7个回答

129

你可以直接将模拟赋给静态方法

import A from '../src/a'
import B from '../src/b'

jest.mock('../src/a')

describe('Wallet', () => {
    it('should work', () => {
        const mockStaticF = jest.fn().mockReturnValue('worked')

        A.staticF = mockStaticF

        const b = new B()

        const result = b.gCallsStaticF()
        expect(result).toEqual('worked')
    })
})

2
然后你也可以进行 expect(mockStaticF).toBeCalled() 类型的断言,尽管绑定版本不起作用(例如 expect(A.staticF).toBeCalled() 会抛出异常) - David A
2
在这种情况下为什么要使用bind?难道不能简单地将mockStaticF分配给A.staticF吗?例如:A.staticF = mockStaticF - Todd Drinkwater
1
可以在“describe”之外模拟“staticF”,例如:jest.mock('../src/a', () => { const A = jest.fn().mockImplementation(() => { f: () => { return 'mockedA.f()'} } }); A.staticF = jest.fn(); return A; }) - frank
1
当您只需要模拟静态函数一次时,可以稍微调整此解决方案: const mockStaticF = jest.fn(A.staticF).mockImplementationOnce(...); A.staticF = mockStaticF; - sKopheK
12
这里有两个问题,它们没有考虑到Jest的工作方式。如果能避免,永远不要使用类似A.staticF = mockStaticF这样的分配方法来模拟方法,否则会阻止Jest在必要时还原方法并潜在地导致测试交叉污染,这就是jest.spyOn的用途。 jest.mock('../src/a')会自动进行模拟,它已经将静态方法作为存根,从而允许A.staticF.mockReturnValue('worked') - Estus Flask
显示剩余2条评论

21
希望这能对你有所帮助
// code to mock
export class AnalyticsUtil {
    static trackEvent(name) {
        console.log(name)
    }
}

// mock
jest.mock('../src/AnalyticsUtil', () => ({
    AnalyticsUtil: {
        trackEvent: jest.fn()
    }
}))
// code to mock
export default class Manager {
    private static obj: Manager
    
    static shared() {
        if (Manager.obj == null) {
            Manager.obj = new Manager()
        }
        return Manager.obj
    }
    
    nonStaticFunc() {
    }
}

// mock
jest.mock('../src/Manager', () => ({
    shared: jest.fn().mockReturnValue({
        nonStaticFunc: jest.fn()
    })
}))
// usage in code
someFunc() {
    RNDefaultPreference.set('key', 'value')
}

// mock RNDefaultPreference
jest.mock('react-native-default-preference', () => ({
    set: jest.fn()
}))
// code to mock
export namespace NavigationActions {
    export function navigate(
      options: NavigationNavigateActionPayload
    ): NavigationNavigateAction;
}

// mock
jest.mock('react-navigation', () => ({
    NavigationActions: {
        navigate: jest.fn()
    }
}))

非常有用的代码片段!谢谢! - friederbluemle

5

我成功地在__mocks__文件夹中使用原型制作了一个模拟对象。因此,您可以这样做:

function A() {}
A.prototype.f = function() {
    return 'a.f()';
};
A.staticF = function() {
    return 'A.staticF()';
};
export default A;

4

这里有一个使用ES6 import的示例。

import { MyClass } from '../utils/my-class';
const myMethodSpy = jest.spyOn(MyClass, 'foo');

describe('Example', () => {
    it('should work', () => {
        MyClass.foo();
        expect(myMethodSpy).toHaveBeenCalled();
    });
 });

2
我们需要创建一个模拟对象,并使模拟方法对测试套件可见。下面是完整的解决方案,包括注释。
let mockF; // here we make variable in the scope we have tests
jest.mock('path/to/StaticClass', () => {
  mockF = jest.fn(() => Promise.resolve()); // here we assign it
  return {staticMethodWeWantToMock: mockF}; // here we use it in our mocked class
});

// test
describe('Test description', () => {
  it('here our class will work', () => {
    ourTestedFunctionWhichUsesThisMethod();
    expect(mockF).toHaveBeenCalled(); // here we should be ok
  })
})

1

Jest 间谍

我选择使用 jest.spyOn

示例

encryption.ts

export class Encryption {
  static encrypt(str: string): string {
     // ...
  }

  static decrypt(str: string): string {
    // ...
  }
}

property-encryption.spec.ts

import { Encryption } from './encryption'
import { PropertyEncryption } from './property-encryption'

describe('PropertyEncryption', () => {
  beforeAll(() => {
    jest
      .spyOn(Encryption, 'encrypt')
      .mockImplementation(() => 'SECRET')
    jest
      .spyOn(Encryption, 'decrypt')
      .mockImplementation(() => 'No longer a secret')
  })

  it("encrypts object values and retains the keys", () => {
    const encrypted = PropertyEncryption.encrypt({ hello: 'world' });

    expect(encrypted).toEqual({ hello: 'SECRET' });
  });

  it("decrypts object values", () => {
    const decrypted = PropertyEncryption.decrypt({ hello: "SECRET" });

    expect(decrypted).toEqual({ hello: 'No longer a secret' });
  });
})

1

在模拟构造函数上使用Object.assign可以同时模拟类及其静态方法。这样做可以让您实现与创建带有静态成员的类时相同的结构。

    import A from '../src/a'
    import B from '../src/b'


    jest.mock('../src/a', () =>
      Object.assign(
        jest.fn(
          // constructor
          () => ({
            // mock instance here
            f: jest.fn()
          })),
        { 
         // mock static here
          staticF: jest.fn(),
        }
      )
    )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接