Bs-jest or vanilla Jest?


#1

Hi, community :slight_smile: How do you test your Reason code (provided you test at all)? Do you use bs-jest and test the ML modules, or do you run bsb first and then test the resulting JS?

I have a suspicion that plain Jest could turn out more robust and flexible at the moment, especially if one needs to mock something. Especially if what the said one needs to mock is the Node fs module, which, one hears, might require a careful selection of a mocking solution.

And of course, there might be other considerations, like the unit tests being independent of types. Not that I expect unit tests to catch a lot of bugs that are supposed to be covered by the OCaml type system. But then I probably have too little experience with both types and TDD to know what can and cannot be caught by types.


#2

I would recommend bs-jest because it will be able to work with ReasonML-encoded data like records and variants. And when you’re testing Reason code, you will want to be asserting things about Reason-encoded data.

That said, if you’re compiling to ES6 modules, you’ll need to output CommonJS as well to let Jest run the tests, otherwise you’ll end up with an error when Jest tries to run the ES6 modules and can’t understand them.

You can set up bsconfig.json to output both ES6 and CommonJS modules with the following config:

...
  "package-specs": [
    {
      "module": "commonjs"
    },
    {
      "module": "es6",
      "in-source": true
    }
  ],
...

This will tell bsb to output ES6 in-source alongside the .re files as usual and also CommonJS module in the lib/js/src/ directory. If you point Jest at that directory it should work out fine.


#3

I’ve got bs-jest running fine with es6 output, you just need to have Babel-jest set up right


#4

I recently set up a demo project using bs-jest and es6: https://github.com/jchavarri/example-bsb-jest-es6

The key was to add a regex in transformIgnorePatterns so Jest also process some modules in node_modules: https://github.com/jchavarri/example-bsb-jest-es6/blob/ab9653eea4f7ce4abeb3648b81b7c6c21e9467df/package.json#L35-L37

@benadamstyles Did you manage to set up bs-jest with es6 without this config by chance? I find it annoying because it forces one to manually include all libraries used in the regex. Do you have a demo project with babel-jest?


#5

I am trying to avoid asking people to add a new thing to their build pipeline. BuckleScript already outputs valid ES5, if we’re pulling in Babel I want it to be for a really good reason–not Jest.


#6

You’re right, I haven’t thought about that. I guess I’m accustomed to TypeScript where the output is about the same as the source, minus type annotations (and the ESNext syntax). With Reason, testing JS output would mean coupling my tests with BS optimizations. Not such a good idea, probably.

And thanks for ES6 advice. Might prove really useful.


#7

@yawaramin I 100% agree. In my experience, the biggest upside of ES6 is tree shaking which results in quite a lot of code being removed by bundling tools. For many teams using ES5 is not an option for this reason (or they might just be integrating BuckleScript with an existing ES6 JavaScript codebase) so I think it’s good to provide solutions for all cases.


#8

There’s also the fact that using BuckleScript ES5 for tests in an ES6 codebase means you’re not testing the same code that goes into production, which I would personally not be very comfortable with based on previous experiences with different build tools in JS.


#9

I think I didn’t explain clearly enough :slight_smile: Here is how I envision this working:

  1. We set up bsconfig.json to output both ES6 and CommonJS modules as I explained above
  2. We use the ES6 output modules and an appropriate bundler to tree-shake for production code (no Babel required because, apart from imports and exports, all the other code is valid ES5)
  3. We use Jest to run the CommonJS unit test modules

As to your point about testing the exact same code that goes into production, that is valid to an extent, but remember that Jest is about unit tests, i.e. we want to test at the unit level, not across module boundaries which might be affected by any potential ES6/CommonJS module differences.


#10

As to your point about testing the exact same code that goes into production, that is valid to an extent, but remember that Jest is about unit tests

Yeah, good point.

Another concern I have about the “dual approach” is related to compilation time, have you measured by chance if having BuckleScript producing both outputs has any impact in that sense?


#11

Oh, wait, I missed this part. You’re totally right, but if we don’t need Babel, then it might turn out that configuring Jest with ES6 is just a matter of adding transform-es2015-modules-commonjs to the config. I’ll see if I can update that sample repo and report back :slight_smile:


#12

I haven’t tried it on anything significant, just a skeleton project to verify it works before my original post. But I would be very surprised if it had any impact on compilation times–it should be just a matter of modifying the imports and exports slightly.

Regarding transform-es2015-modules-commonjs, that seems to be a Babel plugin–would you be able to use that if you weren’t using Babel?


#13

Regarding transform-es2015-modules-commonjs, that seems to be a Babel plugin–would you be able to use that if you weren’t using Babel?

Yeah, jest depends on jest-config which depends on babel-jest, so that dep is there already.

I updated the repo to remove all Babel deps except the es2015 modules transform. Seems to work fine.

I would be very surprised if it had any impact on compilation times

I measured the difference from using only es6 or both outputs with bsb. I did it just a few times and with a very small repo so it’s not representative, but I couldn’t see any relevant difference, in some cases producing both es6 and commonjs was 200ms faster than es6-only :smile:

I am going to explore the dual path further, thanks for bringing it up and explaining it!