0

I am currently writing tests for a project of mine and I was wondering how to specifically mock an imported function's dependencies. Let me give you an example of what I am trying to achieve:

this is in my module.js and I have 2 functions in it.

const sum = (num1, num2) => {
  return num1 + num2;
};

const performAction = (num1, num2) => {
  return sum(num1, num2);
};

export {sum, performAction}

The implementation of the functions is absolutely irrelevant. The example is just demonstrating how performAction has the sum function as a dependency. How in my test file module.test.js I have code that looks like this:

import { performAction } from "./module";

describe("test performAcction", () => {
  it("work as intended", () => {
    expect(performAction(1, 2)).toEqual(3);
  });
});

I want to replace the implementation of the sum function when I test performAction. I just want to say that I was a solution with dependency injection but I did not like the idea of changing the parameters of my functions, just so I can test them.

I find it astonishing that it's so difficult to do that. Is my approach to testing wrong here at all?

Any feedback and recommendations are welcome.

  • 1
    You can't mock a function that is used in the same module it was defined. This is how JS works. Either separate them or treat as a single unit. It's no different from `const performAction = (num1, num2) => { return num1 + num2; }`. – Estus Flask Feb 20 '21 at 07:45
  • @EstusFlask Well, as I have said, I cannot treat them as simple unit, because this code was just an example. In reality I am dealing with 2 functions that cannot be a single unit, without breaking the single-responsibility principle. The question then is - should I separate all the functions without dependencies to a separate module, so I can mock them in this one? It seems logical to do it that way.. Would that in your opinion constitute the 'better practice'? – Kaloyan Stoyanov Feb 20 '21 at 22:25
  • Yes, they need to be separated, exactly because of this concern. The only alternative in Jest is to define then on some object and use them consistently as methods. It's possible with CommonJS `module.exports` but in ES modules there's no such predefined object so this doesn't suit the case. – Estus Flask Feb 21 '21 at 07:02

1 Answers1

0

Personally what I do in this scenario is still dependency injection, but not as an extra parameter:

const sum = (num1, num2) => {
  return num1 + num2;
};

const performAction = (sum) => (num1, num2) => {
//                    ^^^^^^^^
  return sum(num1, num2);
};

export {sum, performAction}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Ivan
  • 1,317
  • 13
  • 23
  • That's exactly what I was doing initially. It's actually still an extra parameter, but in a curried function way.. Then that's fine but what if I want to export performAction for usage? I have to do declare another function const actualPerformAction = performAction(sum) and then export it as export {actualPerformAction as performAction}, so it's with the same name outside of the module.. If I do so, I would have to declare many functions in the pattern performAction -> actualPerformAction in my module. I wonder if there's an easier way.. – Kaloyan Stoyanov Feb 20 '21 at 03:09
  • @KaloyanStoyanov You're right, yes, and plus you'd actually have to export all three functions, something like export {sum, getPerformAction, performAction}. If you're building a library, I'd mark getPerformAction as \@internal. There are also a couple of options here: https://stackoverflow.com/questions/45111198/how-to-mock-functions-in-the-same-module-using-jest – Ivan Feb 20 '21 at 03:57
  • Yeah, I saw some pattern that was used to separate at least the exports, where you would have a testables object exporting all the curried functions and the rest are exported as normal. I guess there is an argument for the dependency injections because if a module has no more than 6,7 functions, one could argue that a couple of "actual" functions declared along the full ones is not going to be too much trouble. I just saw the link that you sent. It seems to me that if the babel rewire solution works is probably the cleanest one? – Kaloyan Stoyanov Feb 20 '21 at 04:25
  • Actually the reimporting the module into itself also works very well! I think that's probably the way to go, once I understand why it works, I might start using it. – Kaloyan Stoyanov Feb 20 '21 at 04:32
  • @KaloyanStoyanov I haven't tried the idea about reimporting module into itself, but that does seem to work pretty well! – Ivan Feb 20 '21 at 04:46
  • @KaloyanStoyanov Self-import may not work because Jest relies on CommonJS modules that work differently from ESM regarding circular dependencies. Rewire isn't supposed to be workable with Jest because they hack the same module API and compete over it. – Estus Flask Feb 20 '21 at 07:51