Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

625
Visualizações
How to mock functions in the same module using Jest?

What's the best way to correctly mock the following example?

The problem is that after import time, foo keeps the reference to the original unmocked bar.

module.js:

export function bar () {
    return 'bar';
}

export function foo () {
    return `I am foo. bar is ${bar()}`;
}

module.test.js:

import * as module from '../src/module';

describe('module', () => {
    let barSpy;

    beforeEach(() => {
        barSpy = jest.spyOn(
            module,
            'bar'
        ).mockImplementation(jest.fn());
    });


    afterEach(() => {
        barSpy.mockRestore();
    });

    it('foo', () => {
        console.log(jest.isMockFunction(module.bar)); // outputs true

        module.bar.mockReturnValue('fake bar');

        console.log(module.bar()); // outputs 'fake bar';

        expect(module.foo()).toEqual('I am foo. bar is fake bar');
        /**
         * does not work! we get the following:
         *
         *  Expected value to equal:
         *    "I am foo. bar is fake bar"
         *  Received:
         *    "I am foo. bar is bar"
         */
    });
});

I could change:

export function foo () {
    return `I am foo. bar is ${bar()}`;
}

to:

export function foo () {
    return `I am foo. bar is ${exports.bar()}`;
}

but this is pretty ugly in my opinion to do everywhere.

over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

An alternative solution can be importing the module into its own code file and using the imported instance of all of the exported entities. Like this:

import * as thisModule from './module';

export function bar () {
    return 'bar';
}

export function foo () {
    return `I am foo. bar is ${thisModule.bar()}`;
}

Now mocking bar is really easy, because foo is also using the exported instance of bar:

import * as module from '../src/module';

describe('module', () => {
    it('foo', () => {
        spyOn(module, 'bar').and.returnValue('fake bar');
        expect(module.foo()).toEqual('I am foo. bar is fake bar');
    });
});

Importing the module into its own code looks strange, but due to the ES6's support for cyclic imports, it works really smoothly.

over 4 years ago · Santiago Trujillo Relatório

0

The problem seems to be related to how you expect the scope of bar to be resolved.

On one hand, in module.js you export two functions (instead of an object holding these two functions). Because of the way modules are exported the reference to the container of the exported things is exports like you mentioned it.

On the other hand, you handle your export (that you aliased module) like an object holding these functions and trying to replace one of its function (the function bar).

If you look closely at your foo implementation you are actually holding a fixed reference to the bar function.

When you think you replaced the bar function with a new one you just actually replaced the reference copy in the scope of your module.test.js

To make foo actually use another version of bar you have two possibilities :

  1. In module.js export a class or an instance, holding both the foo and bar method:

    Module.js:

    export class MyModule {
      function bar () {
        return 'bar';
      }
    
      function foo () {
        return `I am foo. bar is ${this.bar()}`;
      }
    }
    

    Note the use of this keyword in the foo method.

    Module.test.js:

    import { MyModule } from '../src/module'
    
    describe('MyModule', () => {
      //System under test :
      const sut:MyModule = new MyModule();
    
      let barSpy;
    
      beforeEach(() => {
          barSpy = jest.spyOn(
              sut,
              'bar'
          ).mockImplementation(jest.fn());
      });
    
    
      afterEach(() => {
          barSpy.mockRestore();
      });
    
      it('foo', () => {
          sut.bar.mockReturnValue('fake bar');
          expect(sut.foo()).toEqual('I am foo. bar is fake bar');
      });
    });
    
  2. Like you said, rewrite the global reference in the global exports container. This is not a recommended way to go as you will possibly introduce weird behaviors in other tests if you don't properly reset the exports to its initial state.

over 4 years ago · Santiago Trujillo Relatório

0

fwiw, the solution I settled on was to use dependency injection, by setting a default argument.

So I would change

export function bar () {
    return 'bar';
}

export function foo () {
    return `I am foo. bar is ${bar()}`;
}

to

export function bar () {
    return 'bar';
}

export function foo (_bar = bar) {
    return `I am foo. bar is ${_bar()}`;
}

This is not a breaking change to the API of my component, and I can easily override bar in my test by doing the following

import { foo, bar } from '../src/module';

describe('module', () => {
    it('foo', () => {
        const dummyBar = jest.fn().mockReturnValue('fake bar');
        expect(foo(dummyBar)).toEqual('I am foo. bar is fake bar');
    });
});

This has the benefit of leading to slightly nicer test code too :)

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda