Jest: Coverage flag breaks snapshot test

Created on 19 Sep 2016  路  21Comments  路  Source: facebook/jest

I think it's because of code instrumentation

jest --coverage
    - Snapshot
    + Received
...
    -   onSelect={[Function onSelectHandler]}
    +   onSelect={[Function anonymous]}

Running with jest is just OK.

Source code and func name for onSelectHandler:
jest:

    console.log src/components/pie-chart.js:44
      [Function: onSelectHandler]
    console.log src/components/pie-chart.js:45
      function onSelectHandler(_ref2){var nativeEvent=_ref2.nativeEvent;

      if(!onSelect){
      return;
      }
      onSelect(nativeEvent.index);
      }

And for jest --coverage:

    console.log src/components/pie-chart.js:44
      [Function]
    console.log src/components/pie-chart.js:45
      function (_ref2){var nativeEvent=_ref2.nativeEvent;++__cov_742n7agVtRgakP6RGrGnDGRBizg.f['3'];++__cov_742n7agVtRgakP6RGrGnDGRBizg.s['7'];

      if(!onSelect){++__cov_742n7agVtRgakP6RGrGnDGRBizg.b['1'][0];++__cov_742n7agVtRgakP6RGrGnDGRBizg.s['8'];
      return;
      }else{++__cov_742n7agVtRgakP6RGrGnDGRBizg.b['1'][1];}++__cov_742n7agVtRgakP6RGrGnDGRBizg.s['9'];
      onSelect(nativeEvent.index);
      }

How I can avoid this issue? Force use anonymous functions on every place? 馃槸

Bug

Most helpful comment

+1 @guigrpa
screen shot 2016-11-23 at 7 21 02 pm
That's what I get after running jest --watch just after running jest --coverage (and having updated my snapshots with coverage)

All 21 comments

I've also noticed tests that pass consistently without --coverage break consistently when --coverage is passed. I'm not sure how to inspect the instrumented code, but happy to help out debugging this if I can.

Degugging:

console.log(onSelectHandler);
console.log(onSelectHandler+'');

First line will show function name, second will show function source code.

I think this issue comes from istanbul, I can investigate it tomorrow

cc @dmitriiabramov

Yes, this is istanbul.

Related issue: https://github.com/gotwarlost/istanbul/issues/699

For now, I wanted to point out a hotfix for that. Just wrap your named func into anonymous proxy func:

@@ -44,7 +44,7 @@ const PieChart = ({
   return (
     <RCTPieChart
       chartRotation={chartRotationInRadians}
-      onSelect={onSelectHandler}
+      onSelect={(...args) => onSelectHandler(...args)}
       segments={processedSegments}

And snapshot transforms then to this:

@@ -2,7 +2,7 @@ exports[`PieChart Android should render pie chart 1`] = `
 <RCTPieChart
   chartRotation={-0.2617993877991494}
   innerCircleRadius={0.55}
-  onSelect={[Function onSelectHandler]}
+  onSelect={[Function onSelect]}
   segments={
     Array [
       Object {

However, this is a hack...

yeah.. it seems like there's nothing we can do on the jest side, unless we omit function names in snapshots.
in theory it should be possible for istanbul to keep the names of most of the functions using only static analysis, right?

@dmitriiabramov Take a look on my snippets above...

Recap. Given this handler (AFAIK it should be an anonymous function):

 onSelect={(...args) => onSelectHandler(...args)}

It writes this handler in snapshot:

onSelect={[Function onSelect]}

How can it be?

Yeah we should probably just get rid of function names in the snapshots. I originally liked this a lot but it is causing more trouble than it is worth.

Will be fixed in 16.

Hmmm... doesn't solve the problem completely if for some reason you're snapshotting a React element. It's name will turn Unknown in the snapshot when you enable --coverage.

+1 @guigrpa
screen shot 2016-11-23 at 7 21 02 pm
That's what I get after running jest --watch just after running jest --coverage (and having updated my snapshots with coverage)

In my case I'm using enzyme to snapshot a functional stateless component in React, and I get a failed snapshot assertion with coverage enabled, but it's not Unknown - for me it renders Component:

-     <ListItem
+     <Component
          active={false}
          item={
            Object {
              "id": "thing",
              "name": "stuff",
            }
          }
          onClick={[Function]} />

I'd be happy to isolate a repro case for this setup if it helps at all. The curious part here is that all my other snapshots are fine, it's just this instance of snapshotting a functional stateless component that has this differing output.

@dotfold if you can isolate the test case, please file a new issue with it.

If it helps, I get the Unknown on stateless components too. Could give a hint on where to look for.

@thymikee I'm working on this at the moment https://github.com/springload/mocha-chai-to-jest

If you checkout the repo, set it up and run npm run test:jest:coverage you will get the error with the stateless component StatelessAnimal used inside CatBar component.

I quickly tried to use a statefull component like HatchingAnimal inside CatBar and I can confirm it doesn't trigger the error. The HatchingAnimal component is generated properly.

screen shot 2016-12-13 at 4 16 24 pm

Let me know if this helps.

For stateless components you need to define displayName prop of the Component. See this thread for more background: https://github.com/facebook/jest/issues/1824#issuecomment-250478026

@thymikee Cool it fixed my problem. I'm just wondering how come the "normal" jest testing finds the name properly and why the coverage doesn't?

Also using displayName property could lead to some typo mistakes, etc. Why not just use the component name? Like what "normal" jest seems to do.

yeah this kind of sucks. Istanbul wraps functions with other anonymous functions and we take the function name that node gives to the rendered component :(
~ @cpojer

Node 6 infers function names.

Example:

const Banana = () => {};
console.log(Banana.name); // "Banana"

this wasn't possible in previous versions of node.

It works fine when you aren't using coverage but when you do, here is what istanbul does to your code:

const Banana = (__cov[鈥, () => {});
console.log(Banana.name) // empty

because of how the expression is defined, node cannot infer the name properly. The only way to fix it is in istanbul itself or by using a babel transform that changes const Banana = () => {} to const Banana = function Banana() {}.

const Banana = (__cov[鈥, () => {});
console.log(Banana.name) // empty
@cpojer: because of how the expression is defined, node cannot infer the name properly.

Exactly!

I've recently encountered this issue and I tried to fix it on the istanbul side. I forked babel-plugin-istanbul to fix this, and I put my discovery in here:

https://github.com/istanbuljs/babel-plugin-istanbul/issues/125

Could you folks let me know what you think about my proposal?

Per https://github.com/istanbuljs/babel-plugin-istanbul/issues/125#issuecomment-324523554 this is now fixed in [email protected] but requires jest --no-cache in order to work. @cpojer, can this issue be re-opened? Could file a separate one if need by.

@CGamesPlay you can use jest@^21.3.0-beta.2 and run yarn jest --clearCache. After this, jest should compile your code with the new istanbul instrument code for no problem (no --no-cache is needed).

Was this page helpful?
0 / 5 - 0 ratings