jestjs / jest

Delightful JavaScript Testing.
https://jestjs.io
MIT License
44.05k stars 6.43k forks source link

`TypeError: Cannot read property 'foo' of undefined` when using es6 imports #971

Closed richtier closed 8 years ago

richtier commented 8 years ago

Take the following test:

jest.dontMock('../Tracker');

const tracker = require('../Tracker');
const helpers = require('../Helpers');

describe('Tracker', () => {
  describe('functionUnderTest', () => {
    it('should call functionCalledByFunctionUnderTest', () => {
      tracker.functionUnderTest();
      expect(helpers.functionCalledByFunctionUnderTest).toBeCalled();
    });
  });
});

Which tests the following code:

// Tracker.js
import constants from './Constants';
import helpers from './Helpers';

export function functionUnderTest() {
  helpers.functionCalledByFunctionUnderTest();
}

This results in error in Tracker.js: TypeError: Cannot read property 'functionCalledByFunctionUnderTest' of undefined

When I change the es6 imports to require, in Tracker.js, the test passes.

pakage.json includes:

  "jest": {
    "bail": true,
    "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
    "testFileExtensions": [
      "js",
    ],
    "moduleFileExtensions": [
      "js",
    ],
    "testPathDirs": [
      "<rootDir>/src/"
    ],
    "unmockedModulePathPatterns": [
      "node_modules",
    ]
  }

my library versions are:

"jest-cli": "^12.0.2", "babel-jest": "^12.0.2",

Has anyone seen this before?

cpojer commented 8 years ago

Short answer because of vacation, but use babel-jest and jest.unmock instead of dontMock and it will work. More info can be found on the website and in the latest blog post.

richtier commented 8 years ago

thanks. I made those changes and there was some progress - now the tests files are failing with the same Cannot read property 'foo' of undefined erorr - but now instead in the test file rather than the file under test.

I worked around this by explicitly exporting a default object containing all the exported functions in the relevant files - so:

// Tracker.js
import constants from './Constants';
import helpers from './Helpers';

export function functionUnderTest() {
  helpers.functionCalledByFunctionUnderTest();
}

export default {
    functionUnderTest
}

The tests then work, and the assertions pass. This underlying problem is still there though: my setup doesn't seem to handle default exports automatically.

andrewmclagan commented 8 years ago

+1 cant seem to work out how jest handles default exports

cpojer commented 8 years ago

@richtier unmock is not deep. It seem to me like you'll have to also unmock your Constants and Helpers modules.

@andrewmclagan @richtier The way imports/exports work is based on how babel implements them and has nothing to do with Jest. If you use import and export on both sides, it will just work as you expect. If you use export default on one side, and require on the other, you'll have to do require('Foo').default. This is admittedly a confusing implementation detail of babel. Is there a reason you are using require?

Closing this as it is a question and not a bug in Jest. If there is any clarification we could add to the documentation, please feel free to send a pull request :)

richtier commented 8 years ago

@cpojer thanks for the tips.

FWIW I'm no longer using require - now using es6 import. I'm seeing this behaviour across both require and import. I will look towards babel for the answer :)

cpojer commented 8 years ago

Can you share a repository that highlights the issue that you are running into? I'm pretty sure there is just some confusion going on here about automocking and it should be resolved easily :)

richtier commented 8 years ago

@cpojer sure, will create one tonight.

mike-marcacci commented 7 years ago

Was this ever fixed? I am running into exactly this issue, which is really hard to figure out. I'm on jest 20.0.0, and implicitly using babel-jest (since I have a .babelrc), and autoMock is false.

What's so odd is that the exact same dependency chain is required by other tests and working fine... but two of mine (which were working only a few changes ago) consistently crash with this error on startup.

thymikee commented 7 years ago

@mike-marcacci would you mind providing a minimal repro?

mike-marcacci commented 7 years ago

@thymikee - thanks for the quick reply! I'll see if I can distill this down into a minimal repo, but by brute-force trial-and-error, I was able to get this working by explicitly requiring one of the transitive dependencies:

From

import TestHelper from './_TestHelper';

// our test helper
const helper = new TestHelper(__filename);

// variables to be shared between tests
var context, firstIdOnlyResult, firstIdAndTableResult;

// ...

To

import '../schema';
import TestHelper from './_TestHelper';

// our test helper
const helper = new TestHelper(__filename);

// variables to be shared between tests
var context, firstIdOnlyResult, firstIdAndTableResult;

// ...

If I can't reproduce this in a minimal repo (since it's likely dependent on the complexity of the dependency tree) I'll add you as a collaborator to the effected repo.

mike-marcacci commented 7 years ago

OK, here's a minimal repo that demonstrates my issue perfectly: https://github.com/mike-marcacci/jest-issue-971-demo

thymikee commented 7 years ago

@mike-marcacci this is because of circular dependency in your modules. schema must be imported before X is called, because X imports schema.

mike-marcacci commented 7 years ago

Oh man, you are definitely right... I haven't run into this while using the es6 import syntax before. I guess the whole static analysis thing made me a bit too comfortable and I let my guard down – I should remind myself that they're still require() under the hood.

Anyhow, thanks for the help and sorry for the distraction!

masaeedu commented 6 years ago

@thymikee Shouldn't there be some kind of circular dependency error here instead of a difficult to understand undefined?

mrdulin commented 5 years ago

I hit this issue. Finally, I found it's my stupid mistake. Here is my case: https://github.com/mrdulin/jest-codelab/issues/2

jackielii commented 3 years ago

@mike-marcacci this is because of circular dependency in your modules. schema must be imported before X is called, because X imports schema.

This is exactly my problem.

The error was so puzzling as the app ran just fine but the tests failed. I suppose after webpack, the cyclic dependency was gone...

github-actions[bot] commented 3 years ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.