试图为react.js组件编写单元测试。该组件实现了使用mapbox呈现的映射。但不幸的是,我遇到了一系列问题:
第一个是: TypeError: window.URL.createObjectURL不是一个函数
我通过添加以下代码解决了这个问题:https://github.com/mapbox/mapbox-gl-js/issues/3436:
jest.mock('mapbox-gl/dist/mapbox-gl', () => ({
Map: () => ({})
}))
,然后第一个是: ReferenceError:浅层不是定义的
为了在此基础上解决这个问题:ReferenceError on enzyme import,
1) npm istall酶,
2)增加一行:
import { shallow, render, mount } from 'enzyme'
第三个问题是:从‘酶适配器-反应-15’导入适配器。
由于本文:Could not find declaration file for enzyme-adapter-react-16?,下一步是添加以下代码:
import Adapter from 'enzyme-adapter-react-16'
import enzyme from 'enzyme'
enzyme.configure({ adapter: new Adapter() })
,现在终于有了一个拥有: TypeError:_mapboxGl.default.Map不是构造函数
现在,不幸的是,我在网上找不到有意义的解决方案。
有人有类似的问题吗?
为什么单元测试mapbox如此困难?
也许我做错了,而整个解决方案是垃圾?如果是的话,有谁能提出替代方案呢?
以下是整个测试代码:
import React, { Component } from 'react'
import { shallow, render, mount } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import enzyme from 'enzyme'
enzyme.configure({ adapter: new Adapter() })
import Map from '../Map'
jest.mock('mapbox-gl/dist/mapbox-gl', () => ({
Map: () => ({})
}))
describe('<Map />', ()=>{
let mapWrapper
let mapInstance
const map = (disableLifecycleMethods = false)=>shallow(<Map />,{disableLifecycleMethods})
beforeEach(()=>{
mapWrapper = map()
mapInstance = mapWrapper.instance()
})
afterEach(() => {
mapWrapper = undefined;
mapInstance = undefined;
})
it('renders without crashing', () => {
expect(map().exists()).toBe(true);
})
})
以下是经过测试的组件代码:
import React, { Component } from 'react'
import mapboxgl from 'mapbox-gl'
//Mechanics
import {importContours} from './utilities/importContours'
import {addData} from './utilities/addData'
import {setLegend} from './utilities/setLegend'
//Components
import Searchbar from '../search/Searchbar'
import Tabbar from '../tabbar/Tabbar'
import Legend from '../legend/Legend'
//import Popup from '../popup/Popup'
class Map extends Component {
map
constructor(){
super()
this.state = {
active: null,
fetchData: null,
mapType: 0,
searchedPhrase: ''
}
}
componentDidUpdate() {
this.setMapLayer()
}
componentDidMount() {
mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_TOKEN
this.map = new mapboxgl.Map({
container: 'Map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [16.145136, 51.919437],
maxZoom: 13,
minZoom: 3,
zoom: 5.7,
})
this.map.once('load', () => {})
}
setMapLayer(){
if (!this.map.loaded() || this.state.searchedPhrase === '') return
var contours = importContours(this.state.mapType)
var contoursWithData = addData(contours, this.state.mapType, this.state.searchedPhrase)
contoursWithData.then((data)=>{
var mpSource = this.map.getSource("contours")
if (typeof mpSource === 'undefined')
this.map.addSource('contours', { type: 'geojson', data })
else
this.map.getSource("contours").setData(data)
var mpLayer = this.map.getLayer("contours")
if (typeof mpLayer === 'undefined') {
this.map.addLayer({
id: 'contours',
type: 'fill',
source: 'contours',
layout: {},
paint: {
'fill-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false],
0.8,
0.4
]
}
}, 'country-label-lg')
this.map.addLayer({
id: 'state-borders',
type: 'line',
source: 'contours',
layout: {},
paint: {
'line-color': '#c44cc0',
'line-width': 0.01
}
})
}
var hoveredStateId = null
// When the user moves their mouse over the state-fill layer, we'll update the
// feature state for the feature under the mouse.
this.map.on('mousemove', 'contours', (e) => {
if (e.features.length > 0) {
if (hoveredStateId) {
this.map.setFeatureState(
{ source: 'contours', id: hoveredStateId },
{ hover: false }
)
}
hoveredStateId = e.features[0].id
this.map.setFeatureState(
{ source: 'contours', id: hoveredStateId },
{ hover: true }
)
}
})
// When the mouse leaves the state-fill layer, update the feature state of the
// previously hovered feature.
this.map.on('mouseleave', 'contours', () => {
if (hoveredStateId) {
this.map.setFeatureState(
{ source: 'contours', id: hoveredStateId },
{ hover: false }
)
}
hoveredStateId = null
})
// When the user click their mouse over the layer, we'll update the
this.map.on('click', 'contours', (e) => {
var popupHTML = `<Popover
style = { zIndex: 2, position: 'absolute' }
anchorOrigin={{ vertical: 'center',horizontal: 'center'}}
transformOrigin={{vertical: 'center',horizontal: 'center'}}
>
${e.features[0].id}
</Popover>`
if (e.features.length > 0) {
new mapboxgl.Popup(
{style:"zIndex: 2"},
{closeButton: false, closeOnClick: true}
)
.setLngLat(e.lngLat)
.setHTML(popupHTML)
.addTo(this.map);
}
})
this.setState({
active: setLegend(data)
})
//Set fill
if(this.state.active == null) return
const { property, stops } = this.state.active
this.map.setPaintProperty('contours', 'fill-color', {
property,
stops
})
})
}
handleChange = (newMapType) => {
if (this.state.mapType === newMapType) return
const { searchedPhrase } = this.state
if (typeof searchedPhrase === 'undefined')return
this.setState({mapType:newMapType})
}
handleSearch = (newSearchPhrase) => {
if (typeof newSearchPhrase === 'undefined') return
this.setState({searchedPhrase:newSearchPhrase.toUpperCase()})
}
render(){
return (
<div id="Map">
<Searchbar click={this.handleSearch.bind(this)}/>
<Tabbar click={this.handleChange.bind(this)}/>
<Legend active={this.state.active}/>
</div>
)
}
}
export default Map
发布于 2020-12-26 15:48:40
由于莫罗的回答,我成功地在我的测试文件中直接修复了:
import Map from '@/components/modules/Home/Map/Map'
jest.mock('mapbox-gl/dist/mapbox-gl', () => ({
Map: jest.fn(),
Marker: jest.fn().mockReturnValue({
setLngLat: jest.fn().mockReturnValue({
setPopup: jest.fn().mockReturnValue({
addTo: jest.fn().mockReturnValue({})
})
})
}),
Popup: jest.fn().mockReturnValue({
setHTML: jest.fn().mockReturnValue({ on: jest.fn() })
})
}))
describe('Map', () => {
it('should match snapshot', () => {
// When
const wrapper = shallowMount(Map)
// Then
expect(wrapper).toMatchSnapshot()
})
})
https://stackoverflow.com/questions/59542854
复制相似问题