在前端开发中,测试是不可或缺的一环。而 Enzyme 是 React 应用程序测试的一个常用工具,在测试组件时可以有效地模拟交互和状态,检查组件的输出是否符合预期。Enzyme 为我们提供了多种 API,以帮助测试 React 组件,并在测试中提供正确的上下文和环境。但是,在使用 Enzyme 进行测试时,过多地依赖模拟函数可能会导致我们的测试结果不够尽如人意。本文将介绍 Enzyme 测试中的模拟函数问题,并提供避免使用模拟函数的解决方案。
Enzyme 模拟函数的使用场景
作为一个测试框架,Enzyme 提供了多种类型的测试函数,其中最常见的是模拟函数——这是一个返回由你定义的假值的函数。模拟函数通常被用来代替一个真正的函数,以便在测试中模拟或替换函数的行为。
比如说,我们有如下的组件:
-- -------------------- ---- ------- ----- ----------- ------- --------------- - ------------- - --------------------------- - -------- - ------ - ------- ----------- -- -------------------- ----------------------- --------- -- - -
我们希望在测试该组件时检查 onButtonClick
是否被正确地调用了,我们可以使用一个模拟函数来代替 onButtonClick
:
it('calls the onButtonClick callback when the button is clicked', () => { const onButtonClick = jest.fn(); const wrapper = mount(<MyComponent onButtonClick={onButtonClick} buttonText="Click me" />); const button = wrapper.find('button'); button.simulate('click'); expect(onButtonClick).toHaveBeenCalled(); });
上述测试代码中,我们使用了 jest.fn()
,这是一个由 Jest 提供的创建模拟函数的方法。我们使用这个模拟函数来代替 onButtonClick
,每当模拟函数调用时,我们可以在测试中检查其行为与我们预期的是否一致。
然而,在使用模拟函数时需要注意一些问题。
Enzyme 模拟函数可能引起的问题
模拟函数可能会掩盖潜在的问题
使用模拟函数时,我们有可能会遗漏掉一些潜在的问题,因为模拟函数在测试中可能会掩盖一些实际上存在的问题。比如,在上一个例子中,我们使用了一个模拟函数来代替 onButtonClick
,但没有测试该函数是否真的是在点击按钮之后被调用的。这就意味着,即使在实际的使用中 onButtonClick
没有被正确地调用,我们的测试依然会通过,因为我们的测试使用的是模拟函数。
模拟函数可能会使测试变得脆弱
在测试中过度使用模拟函数可能会导致测试变得容易受到代码更改的影响,这很容易让测试失去它们的价值。考虑下面的代码:
-- -------------------- ---- ------- ----- ----------- ------- --------------- - ------------------- - ------------------ --------- -- ----------- ---------- -- - --------------- ---- --- --- - -------- - ----- - ---- - - ----------- ------ ------------------ - -
在上面的组件中,我们使用了 fetch
来获取数据,当数据到达后,它将被存储在状态中,并用于渲染。测试该组件时,我们可以使用模拟函数来模拟 fetch
并返回假的数据,以便测试组件的逻辑是否正确:
-- -------------------- ---- ------- ----------- ---- --- ------- ------- -- -- - ----- ---- - - ---- ----- -- ------------ - ------------------------------- -- ----------------- ----- -- -- ---------------------- -- -- ----- ------- - ------------------ ---- ---------------------------------------- ----- -------------------------------------------- ---
在上面的测试中,我们使用了一个模拟函数来代替 fetch
,并返回我们预先定义的数据。但是,如果 fetch
函数发生了一些改变,比如我们在 headers
中添加了一些新的设置,那么我们的测试将不再能够运行,因为我们的模拟函数不再符合我们预期的行为。
另外, Enzyme 模拟函数的使用也可能会在大规模项目中导致单测变得臃肿,进而需要进行更多的维护。
模拟函数无法检验回调函数的实现
使用模拟函数测试回调函数时,只能检测它们是否被正常调用,却无法检测回调函数内部具体实现是否正确。
对于下面这段代码:
-- -------------------- ---- ------- ----- ----------- ------- --------------- - ------------------ - ----- - -- - - - --------------------- --------------------------- --- - -------- - ----- - -- - - - ----------- ------ - ------- ---------- ---------- -------------- -- ------------------------- ----------------------- --------- -- - -
我们可以按照上面 jest.fn()
的方式进行测试:
-- -------------------- ---- ------- --------- --- ------------- -------- ---- ------- --------- ---- --- ------ -- --------- -- -- - ----- ------------- - ---------- ----- ------- - ------------------ ----------------------------- ------ ------ ----------------- --- ---- ----- ------ - ----------------------- ------------------------ - ------- - -------- - -- --- -- -- -- -- --- ---------------------------------------------- ---- ---
但是,该测试只检验了回调函数是否被调用,并未验证其内部实现是否正确,比如回调函数是否处理了异常等等。
因此,使用模拟函数测试回调函数仅仅是在戏弄测试数据,而并不能真正保证代码的正确性。
解决方案:避免使用模拟函数
虽然模拟函数可以帮助我们模拟一些复杂的场景,在测试中模拟函数的用途并不大,因此我们应该尽量避免使用模拟函数。那么,如何避免使用模拟函数呢?
对于简单的场景,可以直接测试,而无需使用模拟函数,比如所谓的“Happy Path”情况下一切均正常的场景。使用真实的业务组件,而非模拟,提高测试用例的可信度。
对于复杂的场景,可以考虑使用模拟模块,比如使用 Jest 的
jest.mock()
方法模拟模块的导入,或者使用jest.spyOn()
模拟已存在的方法等等。
举个例子,我们需要测试传入组件中的函数是否正常运行:
const handleSubmit = jest.fn(); const wrapper = mount(<Form onSubmit={handleSubmit} />); const submitButton = wrapper.find('button[type="submit"]'); submitButton.simulate('submit'); expect(handleSubmit).toHaveBeenCalled();
现在我们的测试使用了 jest.fn()
来代替 handleSubmit
函数 相对应,我们可以将其替换为:
import Form from './Form'; jest.mock('./Form', () => () => (<form><button type="submit">Submit</button></form>)); const handleSubmit = jest.fn(); const wrapper = mount(<Form onSubmit={handleSubmit} />); const submitButton = wrapper.find('button[type="submit"]'); submitButton.simulate('submit'); expect(handleSubmit).toHaveBeenCalled();
在这个例子中,我们使用了 jest.mock()
方法,将 Form
组件替换为了一个简单的组件。这个简单组件中只有一个表单和一个提交按钮,这里我们可以添加自定义属性。然后,我们再次测试 handleSubmit
是否被调用,直到该测试用例执行后它被调用。这样,我们就避免了使用模拟函数进行测试,提高了测试的准确性和可信度。
总结
在 Enzyme 测试中,使用模拟函数可以帮助我们模拟一些复杂的实现或场景,但这也会导致我们的测试变得容易受到代码更改的影响。因此,我们应该避免使用模拟函数,尽可能使用真实的业务组件和模块进行测试,从而提高测试的准确性和可信度。如果有必要,我们可以使用模拟模块来帮助测试。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64719931968c7c53b0f796a8