当前位置: 首页 > 科技观察

前端单元测试,为什么不测试“实现细节”?

时间:2023-03-23 10:47:11 科技观察

前言大家好,我是海怪。相信很多同学在写单元测试的时候,最大的烦恼不是测试代码怎么写,而是:“应该测试什么?”、“应该测试多深”、“什么不应该测试”。最近在写React组件的单元测试时,发现了Kent(ReactTestingLibrary的贡献者之一)的《Testing Implementation Details》文章,在“Whynottestcodeimplementationdetails?”这个问题上写的非常好。今天把这篇文章分享给大家。在翻译中,我们会尽量使用更地道的语言,这也意味着会在原文的基础上加一层Buf。如需阅读原文,请点击文末【1】。当我开始使用酶时,我尽量避免使用某些API,例如浅层渲染、instance()、state()和find('ComponentName'),并且在查看其他人的PR时,我也会告诉他们不要使用他们。使用这些API。之所以这样,主要是因为这些API会衡量很多代码实现细节(ImplementationDetails)。那么,很多人会问:为什么不测试代码的实现细节(ImplementationDetails)呢?很简单:测试本身就很难,我们不应该制定那么多规则来让测试变得更复杂。为什么测试“实现细节”不好?为什么测试实现细节不好?主要有两个原因:FalseNegative:重构时,代码运行成功,但测试用例崩溃。误报:当应用程序代码真的崩溃时,测试用例通过了。注:这里的测试是指:“判断软件是否工作”。如果测试通过,那么它是肯定的并且代码可以工作。如果测试失败,则为Negative,代码不可用。而这里的False表示“不正确”,即不正确的测试结果。如果您不了解上述内容,那没关系。让我们一一谈谈。我们先看看这个手??风琴组件(Accordion)://Accordion.jsimport*asReactfrom'react'importAccordionContentsfrom'./accordion-contents'classAccordionextendsReact.Component{state={openIndex:0}setOpenIndex=openIndex=>this.setState({openIndex})render(){const{openIndex}=this.statereturn(

{this.props.items.map((item,index)=>(<>this.setOpenIndex(index)}>{item.title}{index===openIndex?({item.contents}):null}))}
)}}exportdefaultAccordion看到这里,肯定有人会说:为什么你还用过时的类组件而不是函数组件?写作怎么样?别着急,继续往下看,你会发现一些有趣的东西(相信用过Enzymes的人应该能猜到会是什么)。以下是测试上述Accordion组件中“实现细节”的测试代码://__tests__/accordion.enzyme.jsimport*asReactfrom'react'//为什么不使用shadowrender,请看https://kcd.im/shallowimportEnzyme,{mount}from'enzyme'importEnzymeAdapterfrom'enzyme-adapter-react-16'importAccordionfrom'../accordion'//SetEnzymes'AdpaterEnzyme.configure({adapter:newEnzymeAdapter()})test('setOpenIndex正确设置打开索引状态',()=>{constwrapper=mount()expect(wrapper.state('openIndex')).toBe(0)wrapper.instance().setOpenIndex(1)expect(wrapper.state('openIndex')).toBe(1)})test('Accordion使用项目内容呈现AccordionContents',()=>{consthats={title:'最喜欢的帽子',contents:'Fedorasareclassy'}constfootware={title:'FavoriteFootware',contents:'Flipflopsarethebest',}constwrapper=mount()expect(wrapper.find('AccordionContents').props().children).toBe(hats.contents)})相信很多同学都会用Enzyme写出类似上面的代码。好的,现在让我们做点什么……重构中的“假错误”我知道大多数人不喜欢编写测试,尤其是UI测试。原因有成千上万,但我听到最多的一个原因是:大多数人会花费大量时间服务于这些测试代码(指测试实现细节的测试代码)。每次我更改某些内容时,测试都会崩溃!-嗓音。一旦测试代码写得不好,会严重拖累你的开发效率。我们来看看这种测试代码会产生什么样的问题。假设,现在我们要重构这个组件来扩展多个Item,而这个改变只能改变代码的实现,而不影响现有组件的行为。重构后的代码如下所示:.statereturn(
{this.props.items.map((item,index)=>(<>this.setOpenIndex(index)}>{item.title}{openIndexes.includes(index)?({item.contents}):null}))}
)}}把上面的openIndex改成openIndexes,这样Accordion就可以一次显示多个AccordionContents。看起来很完美,在UI的真实使用场景中没有问题,但是当我们回过头来运行测试用例的时候,