假设我有一张 Mapbox 地图。 Mapbox 地图有一个 onclick 处理程序,在 onclick 处理程序的回调中,调用了一个函数:
import { Map } from 'mapbox-gl'
const map = new Map({
container: someContainer,
center,
zoom
})
map.on('click', e => {
const { lng, lat } = e.lngLat
runSomeFunction(lat, lng). // <---- i want to test this function
})
我想用玩笑来测试我的地图。围绕这样一个事实已经存在很多问题:mapbox-gl 在浏览器环境之外很难 失败。虽然有些人试图找到解决方法(修复
window.URL. createObjectURL
问题,使用已失效的mapbox-gl-js-mock存储库,克服开玩笑环境中缺乏GL的问题),但我一直在努力在那个兔子洞里呆了大约两天。是时候采取另一种方法了。
很多人都在谈论模拟地图框,就像这样:
jest.mock('mapbox-gl', () => ({
Map: jest.fn(() => ({
on: jest.fn(),
otherMethods: jest.fn(),
})),
}));
我可以使用这个方法并让我的测试通过。但我无法真正测试任何有趣的行为。如何模拟用户点击地图的行为,并测试我是否
expect(runSomeFunction).toHaveBeenCalledWith(whatever)
?
我真的不知道从哪里开始,因为我找不到实际测试地图行为或事件的笑话单元测试的单个示例。为了找到类似的东西,我还研究了如何在玩笑中测试传单地图事件,但我没有发现任何有用的东西。
我看到问题的两个部分:
runSomeFunction()
对我来说,点击处理程序是匿名函数:
(e) => {
const { lng, lat } = e.lngLat;
runSomeFunction(lat, lng);
}
runSomeFunction()
可以通过单元测试单独进行测试。
点击处理程序可以通过单元测试单独进行测试 如果它被提取到命名函数。
还可以通过触发单击事件来测试单击处理程序 确认它调用了一个嘲笑的
runSomeFunction()
使用正确的参数,或确认单击的最终结果。我
会称这些为
集成测试而不是单元测试,因为它们不测试
隔离的单击处理程序。我喜欢 这种整合与
单元
测试。
MapBox 拥有 关于真实自动化测试的资源 浏览器。
这是一个基本的
Map
玩笑模拟,用于处理事件
mapbox-gl
3.1.2
。
测试环境是带有 babel 编译的 jsdom,根据 jest 文档设置。
// tests/__mocks__/mapbox-gl.js
import EventEmitter from 'events';
import { TextDecoder } from 'util';
// Add `TextDecoder` to globals.
globalThis.TextDecoder = TextDecoder;
const mapboxActual = jest.requireActual('mapbox-gl');
/**
* Mock Map class.
*
* Extends the `EventEmitter` class and adds mock properties & methods.
*
* The real `Map` extends `Evented`, but `Evented.fire()` is now private
* and may be deprecated in the future.
*
* @see https://nodejs.org/api/events.html#class-eventemitter
* @see https://github.com/mapbox/mapbox-gl-js/blob/main/CHANGELOG.md#%EF%B8%8F-breaking-changes-5
*/
class MockMap extends EventEmitter {
// ... Put mock properties & methods here.
}
mapboxActual.Map = MockMap;
export default mapboxActual;
这是一个测试
runSomeFunction()
的简单示例
跟踪间谍,只是因为我不知道更好的确认方法
console.log()
被正确调用。在真实的应用程序中,如果
runSomeFunction()
渲染一些东西,你可以测试它是否出现在
屏幕如预期。
// runSomeFunction.js
export default function runSomeFunction(lat, lng) {
console.log(`Map clicked at ${lat}, ${lng}.`);
}
// map.js
import mapboxgl from 'mapbox-gl';
import runSomeFunction from "./runSomeFunction";
// If you pull in `mapbox-gl` with an html `script` tag instead of
// importing it, you'll need to add `mapboxgl` to the globals for the
// test environment so that it's available here.
mapboxgl.accessToken = 'pk.eyJ1Ijoia2VudHIiLCJhIjoiY2tva3YxOWFzMDdpcDJvcno5ZjBzM3JvNCJ9.ZdH2WahCpmtdiLsNLWZNFQ';
const map = new mapboxgl.Map({
container: 'map', // container ID
center: [ -74.5, 40 ], // starting position [lng, lat]
zoom: 9, // starting zoom
});
map.on('click', (e) => {
const { lng, lat } = e.lngLat;
runSomeFunction(lat, lng);
});
export default map;
// map.test.js
import map from './map';
jest.spyOn(console, 'log');
it('logs click on map', () => {
const testLatLng = {
lat: 39,
lng: -74,
};
// Confirm `console.log` not called before event is fired.
expect(console.log)
.not
.toBeCalled();
// Emit click event.
map.emit(
'click',
{
lngLat: testLatLng,
}
);
// Check result.
// May need delay for async event.
expect(console.log)
.toBeCalledTimes(1);
expect(console.log)
.toBeCalledWith(`Map clicked at ${testLatLng.lat}, ${testLatLng.lng}.`);
});
mapbox-gl
通过script
标签如果您通过 html
mapbox-gl
标签而不是拉入 script
将其导入到map.js
,这是设置全局的示例
测试。
// jest.config.js
/** @type {import('jest').Config} */
const config = {
setupFiles: [
'<rootDir>/tests/setupTests.js'
],
testEnvironment: "jsdom",
};
module.exports = config;
// tests/setupTests.js
import mapboxgl from "mapbox-gl";
globalThis.mapboxgl = mapboxgl;