cks头-不要咬人!
它们旨在帮助您创建更简单,更可靠的测试。在本系列文章中,我将向您展示模拟(或“存根”)React组件时所依赖的模式。
这是一个组件存根的好例子。我使用
jest.mock
,我们将在下面详细介绍。
jest.mock("../src/PostContent", () => ({
PostContent: jest.fn(() => (
<div data-testid="PostContent" />
))
}))
一个典型的React组件存根看起来应该并不复杂。注意非常简单的存根值(
div
)和一个属性data-testid
,该属性使我们很容易在DOM中找到渲染的实例。按照惯例,使用的测试标识符必须与组件名称匹配。在这种情况下为PostContent
。
在研究如何使用它们之前,让我们首先记住什么是模拟,以及为什么甚至可能要使用它们。
什么是模拟?
在JavaScript世界中,术语模拟非常广泛地用于指代任何模拟的实现或测试double。模拟实现只是在测试运行时替换生产代码中其他值的值。他们尝试在要替换的对象的接口上进行操作,因此其余代码就像没有替换一样工作。
您可能要这样做有多种不同的原因。我们将通过示例进行介绍。
如果您有兴趣了解有关模拟实现的更多信息,请阅读Martin Fowler的书Mocks Are Not Stubs。
开玩笑和嘲笑
Jest具有一项功能
jest.mock
,可让您模拟要替换的整个模块。在本教程中,尽管其他方式可以替换JavaScript中的对象,但我还是专注于此功能。
在《精通React测试驱动开发》一书中,我使用ES6命名模块导入来创建测试双打。这种方法提供了更多的灵活性,但是看起来有些黑。
Jest jest.mock
它说模拟可以确保您的测试快速而又不脆弱。
尽管这是事实,但这不是我使用模拟的主要原因。
我使用模拟程序是因为它们可以帮助我保持测试彼此独立。
要了解为什么会发生这种情况,我们来看一个示例。
为什么要摩基?
以下是执行以下
BlogPage
两项操作的组件的列表:它id
从属性获取url
,然后显示具有该属性的PostContent
组件id
。
const getPostIdFromUrl = url =>
url.substr(url.lastIndexOf("/") + 1)
export const BlogPage = ({ url }) => {
const id = getPostIdFromUrl(url)
return (
<PostContent id={id} />
)
}
想象一下,您正在为此组件编写测试,并且所有测试都包含在中
BlogPage.test.js
,这是一个涵盖组件BlogPage
和的单个测试套件PostContent
。
在这一点上,你不需要嘲笑尚未:我们还没有看到
PostContent
它,但考虑到大小BlogPage
,实在是没有必要有两个单独的测试套件,因为BlogPage
它是一般简单PostContent
。
现在想象一下为
BlogPage
y和yPostContent
添加功能。作为一名有才华的开发人员,您每天都会添加越来越多的功能。
保持测试正常运行变得更加困难。每个新测试的设置都更加复杂,并且测试套件开始占用您更多的时间-现在需要支持这一负担。
这是一个常见的问题,我一直在React代码库中看到它。即使最简单的更改也会导致许多测试失败的测试套件。
一种解决方案是拆分测试套件。我们将离开
BlogPage.test.js
并创建一个新的PostContent.test.js
测试,其中应包含专门针对的测试PostContent
。基本思想是放置在其中的任何功能都PostContent
必须在中PostContent.test.js
,放置在其中的任何功能BlogPage
(例如URL解析)都必须在中BlogPage.test.js
。
好的。
但是如果渲染
PostContent
有副作用吗?
export const PostContent = ({ id }) => {
const [ text, setText ] = useState("")
useEffect(() => {
fetchPostContent(id)
}, [id])
const fetchPostContent = async () => {
const result = await fetch(`/post?id=${id}`)
if (result.ok) {
setText(await result.text())
}
}
return <p>{text}</p>
};
测试套件
BlogPage.test.js
必须意识到副作用,并准备好应对它们。例如,他将不得不准备好fetch
答案。
我们试图通过拆分测试套件来避免依赖项仍然存在。
我们的测试组织确实得到了改善,但最终,没有任何事情使我们的测试不那么脆弱。
为此,我们需要一个存根(或模拟)
PostContent
。
在下一部分中,我们将研究如何做到这一点。
真的有必要吗?
顺便说一句,关于从端到端测试过渡到单元测试的几句话。
将测试加倍是您编写单元测试的关键指标。
许多经验丰富的测试人员会立即使用单元测试(和模拟)来启动新项目,因为他们知道随着代码库的增长,他们将面临测试不稳定的问题。
单元测试通常比端到端测试小得多。它们可能很小,以至于通常只需要三到四行代码。这使它们成为社交编码实践(例如结对和整体编程)的理想人选。
即使在进行单元测试时,也不总是需要双重测试-它们只是套件中的另一个工具,您需要知道何时以及如何应用。
在下一部分中,我们将介绍基本的模拟技术。