如何在 React Jest 测试中“模拟”navigator.geolocation
Posted
技术标签:
【中文标题】如何在 React Jest 测试中“模拟”navigator.geolocation【英文标题】:How to "mock" navigator.geolocation in a React Jest Test 【发布时间】: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。
【问题讨论】:
How do I configure jsdom with jest的可能重复 @jordan,你能解释一下为什么你认为它是重复的吗?我查看了该答案并尝试通过使用geolocation 库来解决此问题,该库类似于“节点友好的存根,如'node-localstorage'”。描述的解决方案。但在我的应用程序的上下文中,geolocation.getCurrentPosition
返回未定义,不知道为什么它不起作用。对如何解决这个特定问题的实际解释会更有帮助。
【参考方案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 限制,一个潜在的解决方法是禁用该特定行上的类型。如前所述,这是一个解决方法我得到了我的解决方案来自这个问题:***.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
设置为granted
、denied
、prompt
中的任何一个。
【讨论】:
【参考方案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
【讨论】:
以上是关于如何在 React Jest 测试中“模拟”navigator.geolocation的主要内容,如果未能解决你的问题,请参考以下文章
如何在带有 Jest 的 react-native 中使用模拟的 fetch() 对 API 调用进行单元测试
React:如何模拟 Auth0 以使用 Jest 进行测试
当模拟点击调用调用 promise 的函数时,使用 React 的 Jest 和 Enzyme 进行测试
React Jest:如何模拟返回数据的异步 API 调用?