Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing a ThreeJS application with Jest

I am having difficulties testing a JavaScript file with Jest which encapsulate a lot of interactions with ThreeJS. I first tried not to mock ThreeJS, which did not work :

  ● TestSuite › Should instantiate a renderer attached to a specific element of the DOM

    TypeError: Cannot read property 'getExtension' of null

      36 |      */
      37 |     constructor(containerId: string) {
    > 38 |         this.renderer = new WebGLRenderer({ antialias: true });
      39 |         this.attachRenderer(containerId);
      40 |         this.createCamera();
      41 |         this.createScene();

That's normal, because we are testing in a browser-like environment which has no webgl context . So what I did to solve this problem was mocking three.js.

I then mocked my external module with jest.mock("three");

  ● TestSuite › Should instantiate a renderer attached to a specific element of the DOM

    TypeError: this.renderer.setSize is not a function

      64 |             throw new Error("Cannot find DOM element object matching the specified ID: " + containerId);
      65 |         }
    > 66 |         this.renderer.setSize(window.innerWidth, window.innerHeight);
      67 |         element.appendChild(this.renderer.domElement);
      68 |     }
      69 |

And that's expected behavior, since every mock from jest returns undefined, new WebGLRenderer(); returns undefined and I cannot do anything with it.

My current work-around is to define everything I uses in ThreeJS in my test file :

jest.mock("three", () => ({
    Scene: class Scene {
        public add(): void {
            return;
        }
    },
    WebGLRenderer: class WebGlRenderer {
        public render(): void {
            return;
        }
        public setSize(): void {
            return;
        }
    }
    // And a lot more...
}));

But I am well aware that this is not the optimal solution. Before that I was doing the same thing, but in a file in mocks/three.js ( https://jestjs.io/docs/en/manual-mocks ), which was also working fine but that does not satisfy my need.

Is there a way to test this file properly, without having to write tons of manual mocks of threejs ?

like image 257
Marjorie Broadhurst Avatar asked Jul 13 '18 18:07

Marjorie Broadhurst


Video Answer


1 Answers

I am also working on webgl this and solved this in following way. https://github.com/AmitTeli/webgl-three-test

The summary of the approach is

  1. Move all the global variables like scene, renderer and camera to index.html
  2. Initialize them on loading of the page. e.g. in this reactjs example, they are initialized in componentDidMount() of the root component. Other functions can use this context. (You can check this by running yarn start)
  3. While doing the unit testing, initialize them in setupTest.js. We will need additional dev dependencies of headless webgl - gl and canvas
  4. With this setup now we can run the test in say App.test.js (You can check this by running yarn test)
like image 145
Amit Teli Avatar answered Oct 12 '22 00:10

Amit Teli