我有一个使用create-react-app(使用react-scripts 1.1.4)创建的React 16应用程序,其中包含我们创建的以下组件:
import React, {Component} from 'react';
import './ListNav.css';
const tabs = {
previousIndex: 0
};
function styleStringGenerator(index) {
let styleString = {
leftBase: 'left: ',
widthBase: 'width: '
}
if (index === 0) {
styleString.aggregate = `${styleString.leftBase} 0; ${styleString.widthBase}${tabs.widths[0]}px;`;
} else {
styleString.aggregate = `${styleString.leftBase}${tabs.distanceFromOrigin[index]}px; ${styleString.widthBase}${tabs.widths[index]}px;`;
}
return styleString.aggregate;
}
class ListNav extends Component{
constructor(props){
super(props);
this.handleDataTypeSelection = this.handleDataTypeSelection.bind(this);
this.tabScrollWidth = null;
this.setInputRef = element => {
this.tabScrollWidth = element;
};
}
render(){
const dataTypeSelection = (s) => () => this.handleDataTypeSelection(s);
return(
<div className="tab" ref={this.setInputRef}>
<div className="tab__header" onClick={dataTypeSelection("Addresses")}>
<span className="tab__title">Addresses</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("Hotspots")}>
<span className="tab__title">Hotspot Data</span>
</div>
<div className="tab__header" onClick={dataTypeSelection("PSRs")}>
<span className="tab__title">PSRs</span>
</div>
<div className="tab__underline"></div>
</div>
);
}
componentDidMount(){
tabs.elements = document.querySelectorAll('.tab__header');
tabs.length = tabs.elements.length;
tabs.finalIndex = tabs.length - 1;
tabs.totalWidth = document.querySelector('.tab').scrollWidth;
console.log(document);
tabs.widths = []
tabs.elements.forEach((v, index, array) => {
tabs.widths.push(v.scrollWidth);
});
tabs.distanceFromOrigin = [0];
tabs.widths.forEach((v, index, array) => {
if (index > 0) {
tabs.distanceFromOrigin.push(array[index-1] + tabs.distanceFromOrigin[index-1]);
}
});
let styleString = styleStringGenerator(0);
document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');
document.querySelectorAll('.tab__header').forEach((v, index, array) => v.addEventListener('click', function(){
const currentIndex = index;
if (tabs.previousIndex !== currentIndex) {
const styleString = styleStringGenerator(index);
document.querySelector('.tab__underline').setAttribute('style', styleString);
document.querySelector('.tab__title--active').setAttribute('class', 'tab__title');
this.querySelector('.tab__title').setAttribute('class', 'tab__title tab__title--active');
tabs.previousIndex = (function(){return currentIndex})();
}
}, index));
}
handleDataTypeSelection(s){
this.props.getData(s);
}
}
export default ListNav;
我使用的是Jest 20.0.4,酶3.3.0和酶适配器反应16 1.1.1,并创建了以下测试:
import React from 'react';
import Enzyme from 'enzyme';
import {shallow, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import ListNav from '../components/map-list/list-nav/ListNav';
Enzyme.configure({
adapter: new Adapter()
});
const listNav = shallow(<ListNav/>);
describe('ListNav', () => {
it('ListNav renders without crashing', () => {
expect(listNav).toMatchSnapshot();
});
});
运行测试时,出现以下错误:
TypeError:无法读取null的属性“ scrollWidth”
有问题的行在组件中的componentDidMount()
调用中。代码在以下行失败:
tabs.totalWidth = document.querySelector('.tab').scrollWidth;
因为tabs.totalWidth = document.querySelector('.tab')
的值为null,所以无法读取scrollWidth
。我正在使用shallow(<ListNav/>)
,可以在快照中看到"classname": "tab"
,但是测试似乎找不到它。关于如何更好地实施测试或更好地构建代码的任何想法?
答案 0 :(得分:0)
使用闭包使您的document
依赖性可交换。这样,您可以在单元测试中提供一个模拟。
实际代码中的用法是:
import ListNav from "./ListNav";
...
render(){
return <ListNav/>;
}
在测试中的用途:
import { create } from "./ListNav";
it('should...', ()=>{
const documentMock = { title: "mock title" };
const ListNavWithMock = create(documentMock);
const component = shallow(<ListNavWithMock />);
});
为了支持必须对模块进行如下修改:
import React from "react";
export const create = documentInstance => {
return class ListNav extends React.Component {
render() {
return <div>{documentInstance.title}</div>;
}
};
};
export default create(document);
查看exemple here,其中同时加载了ListNav
和ListNavWithMock
。
document
的新模块来提取依赖于documentHelper.js
api的代码documentHelper
documentHelper
模块与模拟模块交换。示例:
describe('ListNav', () => {
let ListNav ;
let documentHelperMock;
beforeEach(() => {
documentHelperMock= { title: "mock title" };
ListNav= require('inject-loader!./ListNav')({
'.documentHelperMock': {documentHelperMock},
});
});
it('should ...', () => {
const wrapper = shallow(<ListNav/>)
});
});
注意:确保没有在文件顶部导入被测模块(ListNav
)。 require
调用完成了这一部分。
此方法较少侵入性,因为不必以明显的方式修改组件代码以用于测试的目的。通过将文档特定的代码移出组件,这只会使代码更整洁。
此方法也更容易,因为您必须模拟的API是您自己的(documentHelper.UpdateTabs
)。在第一种解决方案中,您的模拟可能必须很复杂(querySelector
及其返回值)。