Search code examples
javascripthtmljestjsjsdom

Mocking JavaScript Math.random with Jest doesn't work


Math.random() always giving random values instead of mocked ones.

index.test.js

jest.spyOn(global.Math, "random").mockReturnValue(0.123456789);

const html = fs.readFileSync(path.resolve(__dirname, "./index.html"), "utf8");

let dom;
let container;

describe("index.html", () => {
  beforeEach(() => {
    dom = new JSDOM(html, { runScripts: "dangerously" });
    container = dom.window.document.body;
  });

  it("renders body paragraph", () => {
    expect(container.querySelector("p")).toBeInTheDocument();
  });
});

index.html

<html lang="en">
  <body>
    <p>title</p>
    <script>
      // not showing mocked value
      console.log(Math.random())
    </script>
  </body>
</html>

Is it possible a new instance is created when loading hence mock isn't picked? Interesting enough mocking setInterval works.

Also tried .mockImplementation(() => 0.123456789) to no avail.

enter image description here


Solution

  • It doesn't look like the mock carries over to JSDOM. To get around this issue, I would personally recommend splitting the script into its own file and then running your test against the script file like the below example. Once you've split them out, just include the script file in your HTML file.

    script.js

    Math.random();
    

    script.spec.js

    describe("script.js", () => {
        let spyOnMathRandom;
    
        beforeEach(() => {
            spyOnMathRandom = jest.spyOn(global.Math, 'random').mockReturnValue(1);
            require('./script');
        });
    
        it("calls math random", () => {
            expect(spyOnMathRandom).toReturnWith(1);
        });
    });
    

    index.html

    <html lang="en">
    <body>
    <p>title</p>
    <script src="script.js"></script>
    </body>
    </html>