Simulando métodos estáticos en broma

4 minutos de lectura

avatar de usuario
clemens

Tengo problemas para simular un método estático en broma. Imagina que tienes una clase A con un método estático:

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

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

Y una clase B que importa A

import A from './a'

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

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

Ahora quiere simular A. Es fácil simular 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()'
  })
})

Sin embargo, no pude encontrar ninguna documentación sobre cómo simular A.staticF. es posible?

avatar de usuario
pedro stonham

Simplemente puede asignar el simulacro al método estático

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')
    })
})

  • Y luego también puedes hacer expect(mockStaticF).toBeCalled() aserciones de tipo, aunque la versión enlazada no funcionará (p. ej. expect(A.staticF).toBeCalled() tiraría)

    – David A.

    2 de diciembre de 2019 a las 11:04

  • Buena solución @Peter Realmente me salvaste el día en 2020. Muchas gracias.

    – Mohit

    19 de enero de 2020 a las 18:08

  • ¿Por qué usar bind en este caso? ¿No podría ser más sencillo simplemente asignar mockStaticF a A.staticF? p.ej A.staticF = mockStaticF

    – Todd Bebe agua

    3 de febrero de 2020 a las 7:28

  • también puede burlarse de ‘staticF’ fuera de ‘describe’, por ejemplo jest.mock('../src/a', () => { const A = jest.fn().mockImplementation(() => { f: () => { return 'mockedA.f()'} } }); A.staticF = jest.fn(); return A; })

    – franco

    30 de abril de 2020 a las 22:08


  • Aquí hay dos problemas que no tienen en cuenta cómo funciona Jest. Nunca te burles de los métodos con asignaciones como A.staticF = mockStaticF si no puede evitar esto, esto evita que Jest restaure los métodos donde sea necesario y potencialmente da como resultado la contaminación cruzada de la prueba, para eso es jest.spyOn. jest.mock('../src/a') hace un simulacro automático, ya hace que el método estático sea un código auxiliar que permite A.staticF.mockReturnValue('worked').

    – Frasco de estus

    8 de febrero de 2021 a las 12:29


avatar de usuario
Alex Minokis

Espero que esto te ayudará

// 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()
    }
}))

  • Fragmentos muy útiles! ¡Gracias!

    – Frieder Bluemle

    8 de enero de 2021 a las 2:27

Me las arreglé para burlarme de él en un archivo separado en el __mocks__ carpeta utilizando prototipos. Así que harías:

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

Usando Object.assign en el constructor simulado permite la simulación simultánea de la clase y sus métodos estáticos. Hacer esto le permite lograr la misma estructura que obtiene al crear una clase con miembros estáticos.

    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(),
        }
      )
    )

Necesitamos crear un simulacro y dar visibilidad al método simulado en el conjunto de pruebas. Debajo de la solución completa con comentarios.

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
  })
})

avatar de usuario
CTS_AE

Espías de broma

Fui con la ruta de usar jest.spyOn.

Ejemplo

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' });
  });
})

avatar de usuario
guzmanoj

Aquí hay un ejemplo con una importación ES6.

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

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

¿Ha sido útil esta solución?