前端测试驱动开发 (TDD)

前端测试驱动开发 (tdd)

测试驱动开发(tdd)因提高代码质量和减少软件开发中的错误而被广泛认可。虽然 tdd 在后端和 api 开发中很常见,但它在前端开发中同样强大。通过在实现功能之前编写测试,前端开发人员可以尽早发现问题,确保一致的用户体验并自信地进行重构。在本文中,我们将在前端开发的背景下探索 tdd,讨论它的好处,并演练使用 react 和 javascript 的示例。

为什么在前端开发中使用 tdd?

前端开发面临着独特的挑战,包括用户交互、渲染组件和管理异步数据流。 tdd 使开发人员能够在每个阶段验证其逻辑、组件和 ui 状态,从而提供帮助。 tdd 在前端的好处包括:

更高的代码质量:首先编写测试可以通过强制模块化来鼓励干净、可维护的代码。

立即学习“前端免费学习笔记(深入)”;

提高开发人员信心:测试在代码投入生产之前捕获错误,减少回归错误。

更好的用户体验:tdd 确保组件和交互按预期工作,从而实现更流畅的用户体验。

重构安全:测试提供了一个安全网,允许开发人员进行重构而不必担心破坏功能。

tdd 在前端如何工作:红-绿-重构循环

tdd 流程遵循一个简单的三步循环:红色、绿色、重构。

红色 – 为新特性或功能编写测试。由于尚未实现任何代码,因此该测试最初应该会失败。

绿色 – 编写通过测试所需的最少代码。
重构 – 清理和优化代码而不改变其行为,确保测试继续通过。

让我们通过在 react 中构建简单搜索组件的示例来应用 tdd。

示例:在 react 中为搜索组件实现 tdd
第 1 步:设置测试环境

要跟随,您需要:

react 用于创建 ui 组件。
用于编写和运行测试的 jest 和 react 测试库。

# install dependenciesnpx create-react-app tdd-search-componentcd tdd-search-componentnpm install @testing-library/react

第 2 步:红色阶段 – 编写失败的测试

假设我们想要构建一个搜索组件,根据用户输入过滤项目列表。我们将首先编写一个测试来检查组件是否正确过滤项目。

// search.test.jsimport { render, screen, fireevent } from "@testing-library/react";import search from "./search";test("filters items based on the search query", () => {  const items = ["apple", "banana", "cherry"];  render();  // ensure all items are rendered initially  items.foreach(item => {    expect(screen.getbytext(item)).tobeinthedocument();  });  // type in the search box  fireevent.change(screen.getbyrole("textbox"), { target: { value: "a" } });  // check that only items containing "a" are displayed  expect(screen.getbytext("apple")).tobeinthedocument();  expect(screen.getbytext("banana")).tobeinthedocument();  expect(screen.querybytext("cherry")).not.tobeinthedocument();});

这就是我们正在做的:

使用项目数组渲染搜索组件。
模拟在搜索框中输入“a”。
断言仅显示过滤后的项目。

现在运行测试将导致失败,因为我们还没有实现搜索组件。这是“红色”阶段。

第 3 步:绿色阶段 – 编写最少代码以通过测试

现在,让我们创建搜索组件并编写测试通过所需的最少代码。

// search.jsimport react, { usestate } from "react";function search({ items }) {  const [query, setquery] = usestate("");  const filtereditems = items.filter(item =>    item.tolowercase().includes(query.tolowercase())  );  return (    
setquery(e.target.value)} />
    {filtereditems.map((item) => (
  • {item}
  • ))}
);}export default search;

在此代码中:

我们使用 usestate 来存储搜索查询。
我们根据查询过滤项目数组。
我们仅渲染与查询匹配的项目。

现在,运行测试应该会导致测试通过的“绿色”阶段。

第四步:重构——提高代码结构和可读性

测试通过后,我们就可以专注于提高代码质量了。一个小的重构可能涉及将过滤逻辑提取到一个单独的函数中,以使组件更加模块化。

// refactored search.jsimport react, { usestate } from "react";function filteritems(items, query) {  return items.filter(item =>    item.tolowercase().includes(query.tolowercase())  );}function search({ items }) {  const [query, setquery] = usestate("");  const filtereditems = filteritems(items, query);  return (    
setquery(e.target.value)} />
    {filtereditems.map((item) => (
  • {item}
  • ))}
);}export default search;

通过重构,代码更加清晰,过滤逻辑更加可复用。运行测试可确保组件仍按预期运行。

用于处理边缘情况的 tdd

在 tdd 中,考虑边缘情况至关重要。在这里,我们可以添加测试来处理诸如空项目数组或与任何项目都不匹配的搜索词之类的情况。
示例:测试边缘情况

test("displays no items if the search query doesn't match any items", () => {  const items = ["apple", "banana", "cherry"];  render();  // type a query that doesn't match any items  fireevent.change(screen.getbyrole("textbox"), { target: { value: "z" } });  // verify no items are displayed  items.foreach(item => {    expect(screen.querybytext(item)).not.tobeinthedocument();  });});test("renders correctly with an empty items array", () => {  render();  // expect no list items to be displayed  expect(screen.querybyrole("listitem")).not.tobeinthedocument();});

这些测试进一步确保我们的组件能够处理异常情况而不会中断。

异步前端代码中的 tdd

前端应用程序通常依赖于异步操作,例如从 api 获取数据。 tdd 也可以应用在这里,尽管它需要在测试中处理异步行为。
示例:测试异步搜索组件

假设我们的搜索组件从 api 获取数据,而不是作为 prop 接收数据。

// asyncsearch.jsimport react, { usestate, useeffect } from "react";function asyncsearch() {  const [query, setquery] = usestate("");  const [items, setitems] = usestate([]);  useeffect(() => {    async function fetchdata() {      const response = await fetch(`https://api.example.com/search?q=${query}`);      const data = await response.json();      setitems(data);    }    if (query) fetchdata();  }, [query]);  return (    
setquery(e.target.value)} />
    {items.map((item) => (
  • {item}
  • ))}
);}export default asyncsearch;

在测试中,我们可以使用 jest.fn() 来模拟 api 响应。

import { render, screen, fireEvent, waitFor } from "@testing-library/react";import AsyncSearch from "./AsyncSearch";global.fetch = jest.fn(() =>  Promise.resolve({    json: () => Promise.resolve(["apple", "banana"]),  }));test("fetches and displays items based on the search query", async () => {  render();  fireEvent.change(screen.getByRole("textbox"), { target: { value: "a" } });  // Wait for the items to be fetched and rendered  await waitFor(() => {    expect(screen.getByText("apple")).toBeInTheDocument();    expect(screen.getByText("banana")).toBeInTheDocument();  });});

前端 tdd 最佳实践

从小处开始:专注于一小部分功能并逐渐增加复杂性。
编写清晰的测试:测试应该易于理解并且与功能直接相关。
测试用户交互:验证用户输入、点击和其他交互。
覆盖边缘情况:确保应用程序优雅地处理异常输入或状态。
用于异步测试的模拟 api:模拟 api 调用以避免测试期间对外部服务的依赖。

结论

测试驱动开发为前端开发带来了众多优势,包括更高的代码质量、减少错误和提高信心。虽然 tdd 需要思维方式和纪律的转变,但它成为一项宝贵的技能,尤其是在处理复杂的用户交互和异步数据流时。遵循 tdd 流程(红色、绿色、重构)并逐渐将其集成到您的工作流程中将帮助您创建更可靠、可维护和用户友好的前端应用程序。

以上就是前端测试驱动开发 (TDD)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1496607.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月19日 18:45:08
下一篇 2025年12月19日 18:45:14

相关推荐

  • 测试数据生成器:提高软件测试效率

    在软件测试中,测试数据的准确性和一致性对于可靠的结果至关重要。然而,手动创建和管理测试数据非常耗时且经常容易出错,尤其是对于大型应用程序。这就是测试数据生成器派上用场的地方。测试数据生成器自动创建测试所需的数据,节省时间,减少手动错误,并确保测试过程尽可能无缝。这篇文章探讨了什么是测试数据生成器、为…

    2025年12月19日
    000
  • 为什么需要类型保护?探索不同类型及其用例

    为什么需要类型保护?探索不同类型及其用例 在 typescript 中,类型保护 在使代码库更加可靠、对开发人员更友好方面发挥着重要作用。它们通过允许开发人员缩小类型来帮助确保类型安全,这有助于减少运行时错误并使代码更易于理解和维护。 什么是类型保护? 类型保护是对类型执行运行时检查的函数表达式,确…

    2025年12月19日
    000
  • 为什么使用 `a.call(b)` 调用 `this.say` 时没有输出?

    为什么不输出? 给定代码中,定义了两个函数 a 和 b: function a(name, age) { this.name = name; this.age = age; this.say = function() { console.log(age); };}function b() { thi…

    2025年12月19日
    000
  • 了解 JavaScript 中的作用域和作用域链

    javascript 开发人员经常会遇到作用域、作用域链、词法环境和不同类型的作用域(全局作用域、函数作用域和局部作用域)等术语。这些概念对于理解变量和函数的行为方式、它们的可访问性以及 javascript 在执行代码时如何找到它们至关重要。在本博客中,我们将分解这些主题,以帮助您掌握 javas…

    2025年12月19日
    000
  • 为什么 try-catch 无法捕获 JavaScript 中的 WebSocket 连接失败异常?

    try-catch 无法捕获 WebSocket 连接失败异常 在 JavaScript 中,try-catch 代码块用于处理代码执行过程中引发的异常。但是,对于 WebSocket 连接失败的情况,try-catch 却失效了。 原因 WebSocket 连接失败是由浏览器或底层网络通信机制引发…

    2025年12月19日
    000
  • ES6 中 const 和 let 的区别:为什么可以重新赋值 const 定义的变量?

    ES6中const和let的区别 在ES6中,可以使用const关键字定义常量,该常量一旦定义后不能被重新赋值。而在上述代码示例中,虽然已经使用const定义了常量PI,但后面使用let PI重新赋值,却不会报错。 这是因为let和const在定义变量时的不同行为导致的: let: 定义let变量后…

    2025年12月19日
    000
  • 在构造函数中使用 setInterval 时,为什么 this 会指向 window 对象?

    在构造函数中使用 setinterval 的常见问题 当我们在构造函数中使用 setinterval 时,可能会遇到 this 指向问题,导致 setinterval 仅执行一次。 这主要是由于 javascript 中 this 的绑定机制。在构造函数中,this 被绑定到新创建的对象。当我们使用…

    2025年12月19日
    000
  • 为什么 JavaScript 中的 try catch 无法捕获 WebSocket 连接失败异常?

    javascript 中 try catch 无法捕获 websocket 连接失败异常 javascript 中的 try catch 块可以捕获代码执行过程中抛出的异常,但它无法捕获 websocket 连接失败的错误。这是因为 websocket 连接失败是一个内部处理事件,并未明确抛出异常。…

    2025年12月19日
    000
  • Detailed explanation of Javascript event loop rules

    众所周知,Javacript是单线程语言,但是为什么我们可以使用异步操作呢?因为异步操作是由具有多线程、多进程能力的浏览器执行的。 Javascript始终运行在单线程上,当异步代码被发现时,就会交给浏览器执行,然后浏览器调用相应的线程或进程,包括http请求、GUI、事件触发等来处理这些异步操作.…

    2025年12月19日
    000
  • 在 React 中构建一款可靠的 Pokémon 游戏:开发者的冒险!

    s:单一职责 – 一只神奇宝贝,一份工作 问题:pokemoncomponent 处理捕捉、战斗和显示分数,违反了 srp。 function pokemoncomponent({ pokemon, oncatch, onbattle, score }) { return ( {poke…

    2025年12月19日
    000
  • 页面组件失灵:当 realtyProcessAccount.customer.nature 为空时如何解决?

    页面组件无反应难题解析 网友提出了一个问题,表示页面组件在 certain 条件下才会产生响应。具体来说,当 realtyprocessaccount.customer.nature 不为空时,组件会出现反应;否则,则不会。 潜在解决方案 根据提供的答案,该问题可能是由于在控制器中声明的 realt…

    2025年12月19日
    000
  • 从基础到高级:逐步掌握角度信号

    为什么角度信号很重要:更好应用的初学者指南 angular signals 代表了 angular 应用程序中状态管理和反应性的革命性方法。这份综合指南将引导您了解有关信号所需了解的所有内容,从基本概念到高级实现。 什么是角度信号? 信号是 angular 16+ 中引入的新原语,它提供了一种处理反…

    2025年12月19日 好文分享
    000
  • 如何用 ag-grid 优雅地实现嵌套行表格?

    如何优雅地处理嵌套行的表格 对于需要显示嵌套数据的表格,前端开发者常常会面临处理嵌套行的挑战。本文旨在探讨一个极佳的解决方案——ag-grid。 ag-grid 简介 ag-grid 是一个强大的 javascript 表格库,它提供了广泛的功能,包括支持嵌套行。使用 ag-grid,您可以轻松地创…

    2025年12月19日
    000
  • 将 Jest 添加到Explainerjs

    本周我们应该向我们的 cli 工具添加测试。迄今为止最令人沮丧的事情。 为什么我选择杰斯特 我是 TDD(测试驱动开发)的坚定支持者。几乎每一段代码都应该被测试。在我的合作期间,我一半以上的时间都花在为我的 PR 编写测试上。我相信这段经历确实帮助我理解了测试的必要性。我很惊讶地发现 JS 和 Ru…

    2025年12月19日
    000
  • 为什么我的 JS 脚本调用报错:Uncaught ReferenceError: $ is not defined?

    js 脚本调用报错:uncaught referenceerror 问题: 网上下载了一个 js 脚本,但在调用时,出现以下报错: uncaught referenceerror: $ is not defined 原因: 这个错误是由于 js 脚本中使用了未声明的变量或函数造成的。具体来说,代码中…

    2025年12月19日
    000
  • Highcharts 广东地图:为什么东莞地名不见了?

    Highcharts 广东地图显示东莞名字 在 Highcharts 广东地图中,如果东莞的名字没有显示,可能是由于以下原因: 容器大小限制:确保地图容器拥有足够的空间,尤其是在东莞区附近,以避免文字重叠或超出边界。字体大小:地图上显示的城市名称通常通过 dataLabels 选项配置。如果 fon…

    2025年12月19日
    000
  • 为什么 JavaScript 会出现 “leida is not defined” 错误?

    错误解决:未定义函数 leida 在尝试调用 leida 函数时,提示 “leida is not defined” 错误,表明该函数在当前作用域中尚未定义。可能是因为您忘记加载包含该函数的 JavaScript 文件,或者脚本加载顺序不正确。 解决方案: 确认您已加载正确的…

    2025年12月19日
    000
  • React 选择 + 自定义样式

    react select 库是开发人员创建可定制下拉组件的流行选择之一。在本文中,我们将讨论如何在 react select 中创建样式或自定义样式,以创建适合应用程序主题和 ui 设计的外观。 为什么选择 react select?react select 可以轻松实现具有搜索、多项选择和可自定义…

    2025年12月19日
    000
  • 您应该随 Web 组件一起发送清单

    除了组件之外,自定义元素清单是您可以在库中提供的最重要的东西。 什么是自定义元素清单 (CEM)? 自定义元素清单是一个架构,旨在记录有关自定义元素/web 组件的元数据,包括属性、属性、方法、事件、槽、css 部分和 css 变量。它获取有关组件的所有信息并将其序列化到项目中的单个 json 文件…

    2025年12月19日
    000
  • Highcharts 广东地图:为什么东莞名称无法显示?

    highcharts 广东地图显示东莞名称 在使用 highcharts 绘制广东地图时,可能会遇到东莞名称无法显示的问题。以下步骤可解决此问题: 查看地图容器大小。如果容器较小,东莞的名称可能会被挤出。请将容器大小调整得更大一些。调整 heightchart 选项。此选项控制图表的高度。将其值设置…

    2025年12月19日
    000

发表回复

登录后才能评论
关注微信