嘲笑不咬人!使用React测试库掌握模拟

本文的翻译是在“ JavaScript中的测试自动化”课程开始时准备的










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



现在想象一下BlogPagey和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

在下一部分中,我们将研究如何做到这一点。



真的有必要吗?



顺便说一句,关于从端到端测试过渡到单元测试的几句话。



将测试加倍是您编写单元测试的关键指标。



许多经验丰富的测试人员会立即使用单元测试(和模拟)来启动新项目,因为他们知道随着代码库的增长,他们将面临测试不稳定的问题。

单元测试通常比端到端测试小得多。它们可能很小,以至于通常只需要三到四行代码。这使它们成为社交编码实践(例如结对和整体编程)的理想人选。


即使在进行单元测试时,也不总是需要双重测试-它们只是套件中的另一个工具,您需要知道何时以及如何应用。



在下一部分中,我们将介绍基本的模拟技术






All Articles