不要快照您的UI组件,进行断言
快照是测试的绝佳工具。它使您能够确保某些结果始终与以前完全相同,如果您要对纯函数进行单元测试,则这绝对有用。 UI组件是(或者应该是)纯函数,因此,为什么本文标题指出我们不应该将其用于UI组件?请允许我解释一下。
问题
让我们想象一下以下情况。您开发了一个卡片组件,用于显示个人博客上的博客帖子的图像和标题。然后,您决定为此组件编写单元测试,以确保它同时显示图像和标题。
这很简单,只需快照一下,您就可以开始使用了,对吧?
让我们写下来:
describe('Card', () => {
it('should show image and title', () => {
const { asFragment } = render(() =>
<Card image={/*some url*/} title="Title of my Post" />)
expect(asFragment()).toMatchSnapshot()
})
})
繁荣现在,您的快照具有整个组件的标记。你被覆盖。
现在,您想向该组件添加一个按钮,以便读者实际上可以转到该帖子并阅读该帖子,因为您知道,您实际上是希望人们阅读您的帖子。您进行更改,启动博客的开发服务器,该服务器就在其中,运行良好。
然后您运行测试,测试将失败…
您阅读了测试说明“应显示图像和标题”,查看博客的开发版本,您清楚地看到图像和标题都在显示,同时还有新的闪亮按钮。
我听到你说:“好吧,别傻了,只是更新快照”
更新快照
没错,我忘了更新快照。现在,我必须查看快照,比较旧标记和新标记,评估更改是否有意并进行更新。
我有一个问题要问您:谁在断言,是您还是您的测试?
一个组件很容易做到,但是如果使用更改后的组件有50个不同的组件,并且所有快照测试都失败了,将会发生什么?
我们编写测试以确保我们的组件能够完成他们需要做的事情,履行其合约。当您做出主张而不是进行测试时,就在交易所角色。从字面上看,这与进行手动测试相同。
另外,这是危险的行为。您的思维定式是:“我进行了标记更改,只需更新快照,无需检查”。这样一来,您就可以进入越野车组件。
测试弹性
我们还可以谈论测试的弹性。测试表明它同时显示图像和标题。尽管快照确实显示了它们两者都在其中,但实际上它的作用远不止于此。快照可确保组件的输出与之前的输出完全相同。这使您的代码库无法进行重构,这当然不是一件好事。
您的测试应该不在乎实现,他们应该在乎结果以及是否符合规范。这样,您可以确保您的测试没有误报。如果图像和标题显示在最终标记上,则该测试将永远不会失败,无论如何实现。
解决方案
我希望到目前为止,您确实了解我关于为什么快照UI不好的原因。
解决方案很简单:提出断言
我同意,几年前这很烦人。但是现在我们有了@ testing-library,其中包含超级惊人的查询,例如 getByText
, getByRole
, 和更多。如果您没有听说过,请看一下。真的很棒
让我们使用它们进行重构:
describe('Card', () => {
it('should show image and title', () => {
const title = "Title of my post"
const url = "some url for the image"
const altText = "description of the image"
const { getByText, getByAltText } = render(() =>
<Card image={url} title={title} />)
getByText(title)
expect(getByAltText(altText)).toHaveAttribute('src', url)
})
})
一些注意事项:
-
有意义的错误消息。 Snapshot可以完成查找组件故障的工作。您就是进行比较的人。您确实得到了一个不错的区别,仅此而已。通过这种重构,现在错误消息实际上告诉您出了什么问题。是否找不到组件,这意味着您以某种方式搞砸了渲染,或者更改了组件的API,并且没有更新测试以涵盖所有更改。
-
没有虚假警报。现在,如果可以通过任何方式更改标记,添加或删除图像和标题之外的任何其他内容,则测试不会失败,并且您可以安全地对此组件进行迭代并对其进行重构以使其在将来变得更好。
-
您正在按用户需求使用组件。提供的查询
dom-testing-library
迫使您像使用用户一样使用组件(例如,在屏幕上寻找文本或寻找图片的替代文本)。
结论
为UI组件编写快照测试比利弊多。它实施了一个抵抗变化的代码库。另一方面,测试其行为并做出特定的断言不会导致错误警报和更有意义的错误消息。
你觉得这个怎么样?在下面的评测中添加主题。让我们一起讨论和学习。