【问题标题】:"Cannot read property _location of null" when using React Apollo in a Jest test case在 Jest 测试用例中使用 React Apollo 时“无法读取属性 _location of null”
【发布时间】:2017-10-19 21:50:00
【问题描述】:

给定以下组件:

export function App() {
  return withApollo(<BrowserRouter>
    <MatchListRouteHandler />
  </BrowserRouter>);
}

// MatchListRouteHandler
export const Query = addTypenameToDocument(gql`
  query GetMatches {
    matches {
      id
    }
  }
`);

export default graphql(Query)(MatchListRouteHandler);

还有测试用例:

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
});

当 Jest 尝试运行测试用例时,我收到以下错误:

/home/dan/match-history-analyser/node_modules/jsdom/lib/jsdom/browser/Window.js:148
      return idlUtils.wrapperForImpl(idlUtils.implForWrapper(window._document)._location);
                                                                              ^

TypeError: Cannot read property '_location' of null
    at Window.get location [as location] (/home/dan/Projects/match-history-analyser/node_modules/jsdom/lib/jsdom/browser/Window.js:148:79)
    at Timeout.callback [as _onTimeout] (/home/dan/Projects/match-history-analyser/node_modules/jsdom/lib/jsdom/browser/Window.js:525:40)
    at ontimeout (timers.js:386:14)
    at tryOnTimeout (timers.js:250:5)
    at Timer.listOnTimeout (timers.js:214:5)

【问题讨论】:

    标签: javascript react-apollo


    【解决方案1】:

    这似乎是因为 Jest 测试进程退出太快了; Apollo 尝试继续请求您在 挂载应用程序后提供的数据,但直到 测试结束后才会出现响应,导致这种神秘导致整个 Jest runner 退出的错误消息。

    这可以通过人为增加相关测试结束前的延迟来解决,即:

    it('renders without crashing', (done) => {
      const div = document.createElement('div');
      ReactDOM.render(<App />, div);
    
      setTimeout(() => done());
    });
    

    请注意,这并不能完全修复错误(实际上,您上面的测试将始终通过),但这确实意味着您的整个测试运行器不会完全崩溃。

    正确答案可能涉及在您的测试中使用Server Side Rendering

    【讨论】:

    • 这个答案很糟糕,我很后悔写它。请不要尝试以这种方式对抗比赛条件 - 请参阅彼得的回答。
    【解决方案2】:

    只是对此的一个注释。我今天遇到了这个错误,也是使用 Apollo + Jest,但我以一种更优雅的方式进行了修复,不确定,但我在 afterEach 中卸载了正在测试的组件。

    beforeEach(() => {
        wrapper = mount(<Root location={ '/route/' } context={context} />);
    })
    
    afterEach(() => {
        wrapper.unmount();
    });
    

    【讨论】:

      【解决方案3】:

      在这样渲染后卸载创建的组件:

      it('renders without crashing', () => {
        const div = document.createElement('div');
        ReactDOM.render(<App />, div);
      
        ReactDOM.unmountComponentAtNode(div); // unmounting the created component.
      });
      

      卸载组件应该会使错误消失。

      【讨论】:

      • 如果组件中发生异步操作或抛出错误,则不起作用。
      【解决方案4】:

      @peter.mouland 的回答对我有用。因为我在测试一个组件可以被 ReactDOM 渲染,所以我这样实现了他的方法:

      describe('<MyComponent />', () => {
        const div = document.createElement('div');
      
        afterEach(() => {
          ReactDOM.unmountComponentAtNode(div);
        });
      
        it('deep renders without crashing', () => {
          ReactDOM.render(<MyComponen />,div);
        });
      });
      

      【讨论】:

        猜你喜欢
        • 2017-08-28
        • 2021-06-09
        • 1970-01-01
        • 2018-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-02
        • 2020-08-13
        相关资源
        最近更新 更多