让测试通过数据库
修复集成测试,并添加一些脚本,在测试运行时清除数据库(DB)。
我们几乎已经完成了持久化功能的集成测试修复工作。回想一下,我们已经:
- 编写了集成测试,验证任务在会话之间能够持久保存;
- 为 API 函数和实现所需功能的
useTasks钩子编写了单元测试; - 实现了 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 开发自定义匹配器,并附带几个示例。