4

For example: my math_util.js is

var MathUtil = function(){
  function add(a,b){
    return a + b;
  }
  return {
    add: add
  };
}

I'll use Jest to test add(). So I'll write

test('add', ()=>{
  expect(MathUtil().add(1,1)).toBe(2);
});

But I get MathUtil is undefined or MathUtil() is not a function.

I also tried to use require() or import. But MathUtil doesn't have module.export or export.

So how to write unit test for javascript revealing module pattern with Jest?

Note: I've a project with all scripts written in revealing module pattern so convert all to ES2015 module may not be practical.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Chanh
  • 43
  • 5
  • Do you even have the `MathUtil` function in scope for your tests? It seems that the test doesn't import it in any way. – Tsvetan Ganev Aug 07 '18 at 08:49
  • How to add `MathUtil` to scope? `MathUtil` doesn't have any export so I don't think I can use import. – Chanh Aug 07 '18 at 09:13
  • If you've written an entire project this way, how do your other files find your utility function? – Jared Smith Aug 07 '18 at 15:17
  • I inherited the project and every single files have been written in this way. So all objects are accessed via `window`. The application has access to `window.MathUtil`, but this doesn't work for Jest. Btw `MathUtil` is just an example to show how codes are written in this project. – Chanh Aug 07 '18 at 22:28
  • @Chanh you'll have to rewrite everything to add exports/imports. There is no `window` in node and top-level var declarations aren't in the global scope the way they are in the browser. Have you though about using an in-browser test runner and phantom? – Jared Smith Aug 08 '18 at 09:53
  • 1
    @JaredSmith for the sake of completeness: the default Jest environment is jsdom which includes a `window` object and it is possible to use [setupFiles](https://jestjs.io/docs/en/configuration.html#setupfiles-array) to set up things like global variables...that said, I agree that modules are definitely best practice. – Brian Adams Aug 08 '18 at 15:50
  • @Chanh what was the aswer to this? – japes Sophey Mar 11 '20 at 14:03

2 Answers2

3

If you really want to test math_util.js exactly as it is written you can do this:

// ---- math_util.test.js ----
const fs = require('fs');
const path = require('path');
const vm = require('vm');

const code = fs.readFileSync(path.join(__dirname, '/math_util.js'), 'utf-8');
const MathUtil = vm.runInThisContext(code + '; MathUtil');

test('add', ()=>{
  expect(MathUtil().add(1,1)).toBe(2);
});

...but best practice would be to refactor the code into modules. For the revealing module pattern that should be a very straightforward process, just remove the outer wrapping function and returned object, and put export in front of anything that was in the returned object:

// ---- math_utils.js ----
export function add(a,b){
  return a + b;
}


// ---- math_utils.test.js ----
import { add } from './math_utils';

test('add', ()=>{
  expect(add(1,1)).toBe(2);
});
Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • So there's no way to add `MathUtil` to `window` for Jest to access like calling `window.MathUtl` inside `test()`? I'm afraid that updating every files to have `module.exports` is the only option. – Chanh Aug 07 '18 at 22:35
  • 1
    While the information in this answer is correct, it doesn't actually answer the question. – Jared Smith Aug 08 '18 at 09:54
  • @Chanh I updated my answer with how to test it without modifying math_utils.js – Brian Adams Aug 08 '18 at 15:34
0

You can use babel-plugin-rewire for this. Check this post: https://www.samanthaming.com/journal/2-testing-non-exported-functions/

thomasaull
  • 31
  • 2