【问题标题】:How to "mock" navigator.geolocation in a React Jest Test如何在 React Jest 测试中“模拟”navigator.geolocation
【发布时间】:2017-08-17 22:45:25
【问题描述】:

我正在尝试为我构建的一个反应组件编写测试,该组件在这样的方法中使用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 的支持的信息。上面提到的库在我的反应应用程序中不起作用。即使库本身已正确导入并且在 App 类的上下文中可用,使用特定方法 getCurrentPosition 只会返回 undefined。

【问题讨论】:

  • @jordan,你能解释一下为什么你认为它是重复的吗?我查看了该答案并尝试通过使用geolocation 库来解决此问题,该库类似于“节点友好的存根,如'node-localstorage'”。描述的解决方案。但在我的应用程序的上下文中,geolocation.getCurrentPosition 返回未定义,不知道为什么它不起作用。对如何解决这个特定问题的实际解释会更有帮助。

标签: unit-testing reactjs jestjs create-react-app


【解决方案1】:

似乎已经有一个 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

【讨论】:

  • 当我尝试您的解决方案时出现此错误:'TypeError: Cannot set property 'geolocation' of undefined' 我试图这样定义它:“global.navigator = { userAgent:'node.js ' };"我有这个错误“ReferenceError:未定义初始化”。我不知道该怎么做才能解决这个问题。你知道在我的情况下该怎么做吗?
  • 这个解决方案仍然对我有用。确保在 src/setupTests.js github.com/facebook/create-react-app/blob/master/packages/… 中定义它
  • 最新文档可以在这里找到:facebook.github.io/create-react-app/docs/…
【解决方案2】:

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

当你做这个模拟时: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

【讨论】:

  • 工作就像一个魅力
  • 太棒了!谢谢你!但是你也知道如何触发error函数吗?
  • 对于 TS,它给出了一个错误:“不能分配给 'geolocation' 因为它是一个只读属性。”
  • @GreatQuestion 对于只读限制,我相信这是一个 TypeScript 限制,一个潜在的解决方法是禁用该特定行上的类型。如前所述,这是一个解决方法我得到了我的解决方案来自这个问题:stackoverflow.com/questions/55712640/…
【解决方案3】:

模拟setupFiles

// __mocks__/setup.js

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

然后在你的package.json

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

【讨论】:

    【解决方案4】:

    我按照上面@madeo 的评论来模拟global.navigator.geolocation。成功了!

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

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

    根据要求将state 设置为granteddeniedprompt 中的任何一个。

    【讨论】:

      【解决方案5】:

      TypeScript 版本,适用于任何人 Cannot assign to 'geolocation' because it is a read-only property.

      mockNavigatorGeolocation.ts 文件中(这可以位于test-utils 文件夹或类似文件夹中)

      export const mockNavigatorGeolocation = () => {
        const clearWatchMock = jest.fn();
        const getCurrentPositionMock = jest.fn();
        const watchPositionMock = jest.fn();
      
        const geolocation = {
          clearWatch: clearWatchMock,
          getCurrentPosition: getCurrentPositionMock,
          watchPosition: watchPositionMock,
        };
      
        Object.defineProperty(global.navigator, 'geolocation', {
          value: geolocation,
        });
      
        return { clearWatchMock, getCurrentPositionMock, watchPositionMock };
      };
      

      然后我将它导入到文件顶部的测试中:

      import { mockNavigatorGeolocation } from '../../test-utils';
      

      然后像这样使用函数:

      const { getCurrentPositionMock } = mockNavigatorGeolocation();
      getCurrentPositionMock.mockImplementation((success, rejected) =>
        rejected({
          code: '',
          message: '',
          PERMISSION_DENIED: '',
          POSITION_UNAVAILABLE: '',
          TIMEOUT: '',
        })
      );
      

      【讨论】:

      • 如果我运行npm test,它会抛出一个错误:'TypeError: Cannot redefine property: geolocation at Function.defineProperty ()'
      • 尝试将configurable: true添加到Object.defineProperty中的选项对象
      【解决方案6】:

      出于某种原因,我没有定义 global.navigator 对象,所以我必须在我的 setupTests.js 文件中指定它

      const mockGeolocation = {
        getCurrentPosition: jest.fn(),
        watchPosition: jest.fn(),
      }
      global.navigator = { geolocation: mockGeolocation }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-01-05
        • 1970-01-01
        • 2021-12-10
        • 1970-01-01
        • 2016-05-10
        • 2020-05-17
        • 2021-02-15
        • 2020-09-16
        相关资源
        最近更新 更多