Logo

让测试通过数据库

修复集成测试,并添加一些脚本,在测试运行时清除数据库(DB)。

概念

让测试通过数据库

修复集成测试,并添加一些脚本,在测试运行时清除数据库(DB)。

我们几乎已经完成了持久化功能的集成测试修复工作。回想一下,我们已经:

  1. 编写了集成测试,验证任务在会话之间能够持久保存;
  2. 为 API 函数和实现所需功能的 useTasks 钩子编写了单元测试;
  3. 实现了 API 函数和 useTasks 钩子。

现在,我们只需要在 App.js 中使用这个钩子,测试就应该通过了:

1// /src/App.js 2import React from 'react'; 3import TaskInput from './components/TaskInput'; 4import TaskList from './components/TaskList'; 5import useTasks from './hooks/useTasks'; 6import './App.css'; 7 8function App() { 9 const [tasks, {createTask, toggleTask}] = useTasks(); 10 11 return ( 12 <div> 13 <TaskInput onSubmit={createTask}/> 14 <TaskList tasks={tasks} onToggleTask={toggleTask}/> 15 </div> 16 ); 17} 18 19export default App;

注意:你不需要保留 App.css 的导入。添加它只是为了方便测试。

紧急修复

到达本节时,我意识到自己犯了一个小错误,而你也很可能照做了。问题出在文件 api/index.js 中。当我们发送创建或更新任务的请求时,必须在请求头中包含 Content-Type: application/json,否则 json-server 会忽略该请求。请创建一个头部对象如下:

1const HEADERS = { 2 'Content-Type': 'application/json', 3 'Accept': 'application/json' 4};

并在 fetch 调用中包含它,例如:

1const result = await fetch(ROOT, { 2 method: 'POST', 3 body: JSON.stringify(newTask), 4 headers: HEADERS, 5}); 6 7// 以及 8 9const result = await fetch(`${ROOT}/${updatedTask.id}`, { 10 method: 'PUT', 11 body: JSON.stringify(updatedTask), 12 headers: HEADERS, 13});

最终测试

现在你可以运行应用(记得先执行 npm run json-server)。你可以自行测试持久化功能:尝试创建任务、完成任务,然后刷新页面。这些任务应该仍然存在。然而,如果你运行集成测试,它们仍会失败。

如果集成测试没有失败,请再运行一次。

这是因为我们在每次运行测试之间没有清空数据库。每次运行集成测试时,都会创建相同的任务,导致数据库被重复数据污染。因此当 Selenium 尝试查找特定任务时,可能会找到旧的任务,从而导致测试失败。

为了解决这个问题,我们将创建一个脚本。脚本是一个文件,其中每一行都是命令提示符的命令。在 macOS/Linux 系统上使用 .sh 文件和 BASH,而在 Windows 上我们使用 .bat 文件。

PowerShell 也可以在 Windows 上使用,但我认为对于这种情况来说过于复杂。

如果你使用的是 macOS/Linux,请在项目根目录下创建一个名为 reset-db.sh 的文件,内容如下:

1#!/bin/bash 2rm -f ./db.json 3cp ./db.json.bak ./db.json

如果你使用的是 Windows,请在项目根目录下创建一个名为 reset-db.bat 的文件,内容如下:

1del db.json >nul 2>&1 2echo f | xcopy db.json.bak db.json

每次运行此脚本时,它都会删除 db.json 文件,并从备份恢复一个全新的文件。这意味着你也需要创建一个备份文件。在项目根目录下创建一个名为 db.json.bak 的文件,内容如下(即 db.json 的初始内容):

1{ 2 "tasks": [ 3 { 4 "id": 1, 5 "label": "Do this", 6 "completed": false 7 } 8 ] 9}

尝试从命令行运行该脚本。你是否看到 db.json 文件被恢复?修改其内容或直接删除文件,以确认脚本正常工作。

为了自动运行此脚本,我们将修改 package.json 文件中的 scripts 部分。找到 integration-tests 条目,并将其改为(macOS/Linux):

1"integration-tests": "reset-db.sh && jest integration-tests && reset-db.sh",

Windows:

1"integration-tests": "reset-db.bat && jest integration-tests && reset-db.bat",

虽然有多种方式可以实现跨平台配置,但这些超出了本课程的范围。

最后,当你成功运行 npm run json-server 和 npm start 命令后,就可以执行 npm run integration-tests,并见证三个集成测试全部通过!恭喜你,给自己点个赞吧!

在首次运行集成测试时,可能会遇到超时问题。这是由于 JavaScript 编译器需要时间生成构建产物。为解决此问题,可再次运行测试,或在 jest.config.js 文件中添加以下行:

1module.exports = { 2 transform: { 3 "^.+\\.jsx?$": "babel-jest" 4 }, 5 testTimeout: 15000 // <<< 这一行 6};

以下是最终完成的项目,供参考 (Github):

No configuration found for ID: 385a759ac6bdf79c2cfaf050bb8caadc3ffceef5f6e21fd693a58cf73c82386f

结语

至此,课程的主要部分已结束。此时,你应该已经熟悉如何为 React 项目设置测试,根据用户故事编写集成测试,从集成测试中提取单元测试,并利用单元测试来实现功能。我们还学习了 Jest 中多种断言方式,如何正确地在测试中渲染 React 组件,Jest 的模拟功能,Selenium 中异步操作的处理方法,以及其他许多内容。

下一课是附加章节。我认为它的重要性或相关性不足以纳入主课程内容,但了解它仍然很有帮助。本节重点介绍如何为 Jest 开发自定义匹配器,并附带几个示例。

在单元测试中模拟模块

附加功能:自定义匹配器