如何在 React Jest 测试中“模拟”navigator.geolocation

IT技术 unit-testing reactjs jestjs create-react-app
2021-05-15 08:51:00

我正在尝试为我构建的 React 组件编写测试,该组件navigator.geolocation.getCurrentPosition()在这样的方法中使用(我的组件的粗略示例):

class App extends Component {

  constructor() {
    ...
  }

  method() {
    navigator.geolocation.getCurrentPosition((position) => {
       ...code...
    }
  }

  render() {
    return(...)
  }

}

我正在使用create-react-app,其中包括一个测试:

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

此测试失败,在控制台中打印出来:

TypeError: Cannot read property 'getCurrentPosition' of undefined

我是 React 的新手,但对 angular 1.x 有相当多的经验。在 angular 中,模拟(在 beforeEach 中的测试中)函数、“服务”和全局对象方法(如 navigator.geolocation.etc)是很常见的。我花时间研究这个问题,这段代码是我能得到的最接近模拟的代码:

global.navigator = {
  geolocation: {
    getCurrentPosition: jest.fn()
  }
}

我把它放在我的 App 测试文件中,但它没有效果。

我怎样才能“模拟”这个导航器方法并让测试通过?

编辑:我研究了使用名为geolocation的库该库据称navigator.getCurrentPosition可以在节点环境中使用。如果我理解正确的话,jest 在节点环境中运行测试并使用 JSDOM 模拟window. 我没能找到很多关于 JSDOM 对navigator. 上面提到的库在我的react应用程序中不起作用。即使库本身已正确导入并且在 App 类的上下文中可用,使用特定方法 getCurrentPosition 也只会返回 undefined 。

4个回答

似乎已经有一个global.navigator对象,和你一样,我无法重新分配它。

我发现模拟地理定位部分并将其添加到现有的global.navigator对我有用。

const mockGeolocation = {
  getCurrentPosition: jest.fn(),
  watchPosition: jest.fn()
};

global.navigator.geolocation = mockGeolocation;

我将它添加到src/setupTests.js文件中,如下所述 - https://create-react-app.dev/docs/running-tests#initializing-test-environment

我知道这个问题可能已经解决了,但似乎上面的所有解决方案都是错误的,至少对我来说是这样。

当你做这个模拟时:getCurrentPosition: jest.fn() 它返回未定义,如果你想返回一些东西,这是正确的实现:

const mockGeolocation = {
  getCurrentPosition: jest.fn()
    .mockImplementationOnce((success) => Promise.resolve(success({
      coords: {
        latitude: 51.1,
        longitude: 45.3
      }
    })))
};
global.navigator.geolocation = mockGeolocation;

我正在使用 create-react-app

嘲讽 setupFiles

// __mocks__/setup.js

jest.mock('Geolocation', () => {
  return {
    getCurrentPosition: jest.fn(),
    watchPosition: jest.fn(),
  }
});

然后在你的 package.json

"jest": {
  "preset": "react-native",
  "setupFiles": [
    "./__mocks__/setup.js"
  ]
}

我按照上面@madeo 的评论来模拟global.navigator.geolocation. 有效!

另外我做了以下模拟global.navigator.permissions

  global.navigator.permissions = {
    query: jest
      .fn()
      .mockImplementationOnce(() => Promise.resolve({ state: 'granted' })),
  };

设置state任何的granteddeniedprompt按要求。