Jest throwing TypeError: Cannot read property '

2019-06-23 05:40发布

问题:

I'm trying to run some tests with Jest on my react/react-native library (only some business logic inside).

We are testing actions that uses fetch function (polyfill with whatwg-fetch). I've added whatwg-fetch (thanks to Safari) for react.

Whenever i try to run a test, i'm getting this error:

TypeError: Cannot read property 'fetch' of undefined

  at node_modules/whatwg-fetch/fetch.js:4:11
  at Object.<anonymous> (node_modules/whatwg-fetch/fetch.js:461:3)
  at Object.<anonymous> (node_modules/jest-expo/src/setup.js:138:416)

What can cause this issue? Is there a way in the jest config to avoid this?

Here are some files for debug:

Jest config in package.json

"jest": {
"preset": "jest-expo",
"moduleFileExtensions": [
  "js",
  "jsx",
  "ts",
  "tsx"
],
"verbose": true,
"transform": {
  "^.+\\.(js|ts|tsx)$": "<rootDir>/node_modules/babel-jest"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
  "\\.snap$",
  "<rootDir>/node_modules/",
  "<rootDir>/dist/"
],
"transformIgnorePatterns": [
  "node_modules/?!react-native"
]
},

Webpack config:

const config = {
entry: [
    'whatwg-fetch',
    __dirname + '/src/index.ts',
],
devtool: 'source-map',
output: {
    path: path.join(__dirname, '/dist'),
    filename: 'index.js',
    library: 'checkinatwork-module',
    libraryTarget: 'umd',
    umdNamedDefine: true,
},
module: {
    loaders: [
        { test: /\.(tsx|ts)?$/, loader: 'ts-loader', exclude: /node_modules/ },
    ],
},
resolve: {
    modules: [
        './src',
        'node_modules',
    ],
    extensions: ['.js', '.ts', '.jsx', '.tsx', 'json'],
},
plugins: [
],
};

Test file:

import expect from 'expect';
import * as actions from '../../src/components/Checkin/checkin.action';
import * as reducers from '../../src/components/Checkin/checkin.reducer';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import nock from 'nock';

const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);

describe('=> ADD CHECKIN ACTIONS', () => {
  describe('- REQUEST', () => {
    it('Action: ADD_CHECKIN_REQUEST should request addCawCheckin', () => {
      const expectedAction = {
        type: actions.ADD_CHECKIN_REQUEST,
        isFetching: true,
      };
      expect(actions.addCheckinRequest())
        .toEqual(expectedAction);
    });
    it('Reducer: newCheckin should trigger ADD_CHECKIN_REQUEST and initiate loading', () => {
      const expectedState = {
        isFetching: true,
        status: null,
      };
      expect(reducers.newCheckin(reducers.newCheckinDefaultState, actions.addCheckinRequest()))
        .toEqual(expectedState);
    });
  });

Action file:

export const getCheckins = (sessionId, date, url, isRefresh) => {
  const config = {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      sessionId: {sessionId},
      date: {date},
    }),
  };

  return dispatch => {
    if (!isRefresh) {
      dispatch(getCheckinsRequest());
    }
    return fetch(url + 'getCAWCheckIns', config)
      .then(response => response.json())
      .then(({ checkins }) => {
        dispatch(getCheckinsSuccess(checkins));
      }).catch(err => {
        dispatch(getCheckinsError('Get checkins failed'));
        console.error('Get checkins failed: ', err);
      });
  };
};

Thanks!

回答1:

I've done it in the spec with:

import { fetch } from 'whatwg-fetch';

global.fetch = fetch;

And it has worked as expected with Jest.



回答2:

might be late to the game, but this worked for me.

Possible solution #1

Note: React.PropTypes is deprecated as of React v15.5. Please use the prop-types library instead.

If you install the npm package prop-types, it has isomorphic-fetch as a dependency. This will give you fetch as a global. You will still need to import it into your test file. You might need to exclude it from your linter too.

add this to the top of the test file.

import fetch from 'isomorphic-fetch'

I didn't need to call fetch in the test suite, but I needed to make it available.

If you use this approach, I think you would remove the 'whatwg-fetch', from your webpack entry

Hope this helps

Updated: Possible solution #2

Using the example of @zvona from above, but create a MOCKS folder in your app. then a file /globalMock.js. You might not have set it up properly.

   __MOCKS__/globalMock.js

   // use one of these imports 

   import { fetch } from 'whatwg-fetch' // if you want to keep using the polyfill

   import { fetch } from 'isomorphic-fetch' // from a dependency node module that I spoke of in the previous solution.

   global.fetch = fetch

Now in package.json

add this to your Jest configuration:

"jest": {
    "verbose": true,
    "rootDir": "app",
    "setupFiles": ["<rootDir>/__MOCKS__/globalMock.js"]
  }

this will allow the use of fetch in your tests.

I also had to use this same concept for localStorage. Where I keep all of my globals that Jest doesn't have access.



回答3:

Upgrading react-native, jest, and babel-jest to the latest versions fixed this issue for us.



回答4:

This worked for me. In your expo set up file(node_modules/jest-expo/src/setup.js) where it requires whatwg-fetch, I changed that require to require('fetch-everywhere')

const { Response, Request, Headers, fetch } = 
  require('fetch-everywhere');
global.Response = Response;
global.Request = Request;
global.Headers = Headers;
global.fetch = fetch;

For some reasons, only fetch everywhere was working with expo and jest.