React 单元测试策略及落地 #一篇就够系列

本文探讨了React应用中单元测试的重要性,特别是在快速开发和响应力提升的背景下。介绍了单元测试的上下文,强调了在敏捷开发中单元测试的作用。文章详细阐述了测试金字塔,提倡写好单元测试,确保每个测试有且只有一个失败理由,具有高表达力和稳定性。针对React应用,提出了具体的单元测试策略,包括actions、reducer、selector、saga和组件的测试,以及如何避免错误的测试姿势。总结了好的单元测试应具备的特征,并提出了针对React组件的测试建议,如不测试redux connect过的组件和UI测试。最后,强调了功能型组件和utils测试的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写好的单元测试,对开发速度、项目维护有莫大的帮助。前端的测试工具一直推陈出新,而测试的核心、原则却少有变化。与产品代码一并交付可靠的测试代码,是每个专业开发者应该不断靠近的一个理想之地。本文就围绕测试讲讲,为什么我们要做测试,什么是好的测试和原则,以及如何在一个 React 项目中落地这些测试策略。

本文使用的测试框架、断言工具是 jest。文章不打算对测试框架、语法本身做过多介绍,因为已有很多文章。本文假定读者已有一定基础,至少熟悉语法,但并不假设读者写过单元测试。在介绍什么是好的单元测试时,我会简单介绍一个好的单元测试的结构。

目录

  1. 为什么要做单元测试
    1. 单元测试的上下文
    2. 测试策略:测试金字塔
    3. 如何写好单元测试:好测试的特征
      • 有且仅有一个失败的理由
      • 表达力极强
      • 快、稳定
  2. React 单元测试策略及落地
    1. React 应用的单元测试策略
    2. actions 测试
    3. reducer 测试
    4. selector 测试
    5. saga 测试
      • 来自官方的错误姿势
      • 正确姿势
    6. component 测试
      • 业务型组件 - 分支渲染
      • 业务型组件 - 事件调用
      • 功能型组件 - children 型高阶组件
    7. utils 测试
  3. 总结
  4. 未尽话题 & 欢迎讨论

为什么要做单元测试

虽然关于测试的文章有很多,关于 React 的文章也有很多,但关于 React 应用之详细单元测试的文章还比较少。而且更多的文章都更偏向于对工具本身进行讲解,只讲「我们可以这么测」,却没有回答「我们为什么要这么测」、「这么测究竟好不好」的问题。这几个问题上的空白,难免使人得出测试无用、测试成本高、测试使开发变慢的错误观点,导致在「质量内建」已渐入人心的今日,很多人仍然认为测试是二等公民,是成本,是锦上添花。这一点上,我的态度一贯鲜明:不仅要写测试,还要把单元测试写好;不仅要有测试前移质量内建的意识,还要有基于测试进行快速反馈快速开发的能力。没自动化测试的代码不叫完成,不能验收。

「为什么我们需要做单元测试」,这是一个关键的问题。每个人都有自己关于该不该做测试、该怎么做、做到什么程度的看法,试图面面俱到、左右逢源地评价这些看法是不可能的。我们需要一个视角,一个谈论单元测试的上下文。做单元测试当然有好处,但本文不会从有什么好处出发来谈,而是谈,在我们在意的这个上下文中,不做单元测试会有什么问题。

那么我们谈论单元测试的上下文是什么呢?不做单元测试我们会遇到什么问题呢?

单元测试的上下文

先说说问题。最大的一个问题是,不写单元测试,你就不敢重构,就只能看着代码腐化。代码质量谈不上,持续改进谈不上,个人成长更谈不上。始终是原始的劳作方式。

image

image

再说说上下文。我认为单元测试的上下文存在于「敏捷」中。现代企业数学化竞争日益激烈,业务端快速上线、快速验证、快速失败的思路对技术端的响应力提出了更高的要求:更快上线更频繁上线持续上线。怎么样衡量这个「更快」呢?那就是第一图提到的 lead time,它度量的是一个 idea 从提出并被验证,到最终上生产环境面对用户获取反馈的时间。显然,这个时间越短,软件就能越快获得反馈,对价值的验证就越快发生。这个结论对我们写不写单元测试有什么影响呢?答案是,不写单元测试,你就快不起来。为啥呢?因为每次发布,你都要投入人力来进行手工测试;因为没有测试,你倾向于不敢随意重构,这又导致代码逐渐腐化,复杂度使得你的开发速度降低。

再考虑到以下两个大事实:人员会流动,应用会变大。人员一定会流动,需求一定会增加,再也没有任何人能够了解任何一个应用场景。因此,意图依赖人、依赖手工的方式来应对响应力的挑战首先是低效的,从时间维度上来讲也是不现实的。那么,为了服务于「高响应力」这个目标,我们就需要一套自动化的测试套件,它能帮我们提供快速反馈、做质量的守卫者。唯解决了人工、质量的这一环,效率才能稳步提升,团队和企业的高响应力才可能达到。

那么在「响应力」这个上下文中来谈要不要单元测试,我们就可以很有根据了,而不是开发爽了就用,不爽就不用这样含糊的答案:

  • 如果你说我的业务部门不需要频繁上线,并且我有足够的人力来覆盖手工测试,那你可以不用单元测试
  • 如果你说我是个小项目小部门不需要多高的响应力,每天摸摸鱼就过去了,那你可以不用单元测试
  • 如果你说我不在意代码腐化,并且我也不做重构,那你可以不用单元测试
  • 如果你说我不在意代码质量,好几个没有测试保护的 if-else 裸奔也不在话下,脑不好还做什么程序员,那你可以不用单元测试
  • 如果你说我确有快速部署的需求,但我们不 care 质量问题,出回归问题就修,那你可以不用单元测试

除此之外,你就需要写单元测试。如果你想随时整理重构代码,那么你需要写单元测试;如果你想有自动化的测试套件来帮你快速验证提交的完整性,那么你需要写单元测试;如果你是个长期项目有人员流动,那么你需要写单元测试;如果你不想花大量的时间在记住业务场景和手动测试应用上,那么你就需要单元测试。

至此,我们从「响应力」这个上下文中,回答了「为什么我们需要写单元测试」的问题。接下来可以谈下一个问题了:「为什么是单元测试」。

测试策略:测试金字塔

上面我直接从高响应力谈到单元测试,可能有的同学会问,高响应力这个事情我认可,也认可快速开发的同时,质量也很重要。但是,为了达到「保障质量」的目的,不一定得通过测试呀,也不一定得通过单元测试鸭。

这是个好的问题。为了达到保障质量这个目标,测试当然只是其中一个方式,稳定的自动化部署、集成流水线、良好的代码架构、组织架构的必要调整等,都是必须跟上的设施。我从未认为单元测试是解决质量问题的银弹,多方共同提升才可能起到效果。但相反,也很难想象单元测试都没有都写不好的项目,能有多高的响应力。

即便我们谈自动化测试,未必也不可能全部都是写单元测试。我们对自动化测试套件寄予的厚望是,它能帮我们安全重构已有代码保存业务上下文快速回归。测试种类多种多样,为什么我要重点谈单元测试呢?因为~~这篇文章主题就是谈单元测试啊…~~它写起来相对最容易、运行速度最快、反馈效果又最直接。下面这个图,想必大家都有所耳闻:

image

这就是有名的测试金字塔。对于一个自动化测试套件,应该包含种类不同、关注点不同的测试,比如关注单元的单元测试、关注集成和契约的集成测试和契约测试、关注业务验收点的端到端测试等。正常来说,我们会受到资源的限制,无法应用所有层级的测试,效果也未必最佳。因此,我们需要有策略性地根据收益-成本的原则,考虑项目的实际情况和痛点来定制测试策略:比如三方依赖多的项目可以多写些契约测试,业务场景多、复杂或经常回归的场景可以多写些端到端测试,等。但不论如何,整个测试金字塔体系中,你还是应该拥有更多低层次的单元测试,因为它们成本相对最低,运行速度最快(通常是毫秒级别),而对单元的保护价值相对更大。

以上是对「为什么我们需要的是单元测试」这个问题的回答。接下来一小节,就可以正式进入如何做的环节了:「如何写好单元测试」。

关于测试金字塔的补充阅读请加我q3177181324。

如何写好单元测试:好测试的特征

写单元测试仅仅是第一步,下面还有个更关键的问题,就是怎样写出好的、容易维护的单元测试。好的测试有其特征,虽然它并不是什么新的东西,但总需要时时拿出来温故知新。很多时候,同学感觉测试难写、难维护、不稳定、价值不大等,可能都是因为单元测试写不好所导致的。那么我们就来看看,一个好的单元测试,应该遵循哪几点原则。

首先,我们先来看个简单的例子,一个最简单的 JavaScript 的单元测试长什么样:

// production code
const computeSumFromObject = (a, b) => {
  return a.value + b.value
}

// testing code
it('should return 5 when adding object a with value 2 and b with value 3', () => {
  // given - 准备数据
  const a = { value: 2 }
  const b = { value: 3 }

  // when - 调用被测函数
  const result = computeSumFromObject(a, b)

  // then - 断言结果
  expect(result).toBe(5)
})

以上就是一个最简答的单元测试部分。但麻雀虽小,五脏基本全,它揭示了单元测试的一个基本结构:准备输入数据、调用被测函数、断言输出结果。任何单元测试都可以遵循这样一个骨架,它是我们常说的 given-when-then 三段式。

为什么说单元测试说来简单,做到却不简单呢?除了遵循三段式,显然我们还需要遵循一些其他的原则。前面说到,我们对单元测试寄予了几点厚望,下面就来看看,它如何能达到我们期望的效果,以此来反推单元测试的特征:

  • 安全重构已有代码 -> 应该有且仅有一个失败的理由不关注内部实现
  • 保存业务上下文 -> 表达力极强
  • 快速回归 -> 稳定

下面来看看这三个原则都是咋回事:

有且仅有一个失败的理由

有且仅有一个失败的理由,这个理由是什么呢?是 「当输入不变时,当且仅当被测业务代码功能被改动了」时,测试才应该挂掉。为什么这会支持我们重构呢,因为重构的意思是,在不改动软件外部可观测行为的基础上,调整软件内部实现的一种手段。也就是说,当我被测的代码输入输出没变时,任我怎么倒腾重构代码的内部实现,测试都不应该挂掉。这样才能说是支持了重构。有的单元测试写得,内部实现(比如数据结构)一调整,测试就挂掉,尽管它的业务本身并没修改,这样怎么支持重构呢?不怪得要反过来骂测试成本高,没有用。一般会出现这种情况,可能是因为是先写完代码再补的测试,或者对代码的接口和抽象不明确所导致。

另外,还有一些测试(比如下文要看到的 saga 官方推荐的测试),它需要测试实现代码的执行

<think>嗯,用户让我一个技术解决方案文档,关于AI智能小助手的,需要处理知识库回答和数据统计图表展示。首先,我需要理清楚需求的核心点。系统相关的知识库内容,比如制度、规则、条款,这部分需要问答功能。然后数据统计,比如查询销售额,还要图表展示。用户提到用开源组件,比如dify、supersonic,大模型用deepseek、qianwen这些。 首先,我应该拆分模块。可能分为知识库问答模块和数据可视化模块。知识库这边需要处理文档的存储、向量化、检索。数据可视化需要对接数据库,执行查询,生成图表。然后前端可能需要一个界面,用户输入问题或查询,后端处理返回结果。 知识库方面,可能需要用向量数据库,比如Milvus或者FAISS。文档需要预处理,分块,embedding,然后存到向量库。用户提问时,先检索相关文档片段,再让大模型生成回答。这里可能需要用LangChain这样的框架来处理流程,或者用Dify这样的平台简化开发。 数据统计部分,用户可能会用自然语言查询,比如“今年东南区域的销售额”,需要转换成SQL查询。可能需要用NL2SQL的技术,或者用预定义的查询模板。然后执行查询,用Echarts或Matplotlib生成图表。Superset或Metabase这样的开源工具可以集成进来做可视化。 大模型的选择,用户提到了deepseek和qianwen,这两个都是国内可用的模型。需要考虑API的接入成本、响应速度、准确性。可能还需要做微调,特别是在处理特定领域术语的时候。 系统架构方面,前端可以用Web框架,比如Vue或React,后端用Flask或FastAPI。数据库可能需要关系型数据库存业务数据,向量数据库存知识库。然后需要API网关来管理请求路由。 安全性方面,用户权限控制,数据加密,防止SQL注入这些都要考虑。性能优化方面,缓存机制,异步处理,负载均衡可能需要用到。 开源组件方面,Dify好像是一个LLM应用开发平台,可以快速搭建问答系统。Supersonic是啥?可能需要查一下,可能是另一个开源工具。如果Dify能处理知识库问答,那可能节省很多开发时间。数据可视化部分,用Apache ECharts或者Superset。 然后需要设计实现步骤,分阶段开发,先做知识库模块,再做数据统计,然后整合测试。部署可能需要Docker容器化,用K8s管理。 可能遇到的挑战:自然语言转SQL的准确性,知识库更新后的实时检索,大模型的回答准确性,以及系统的响应速度。需要评估不同模型的效果,做一些测试,可能需要RAG(检索增强生成)来提升回答质量。 还要考虑用户权限,不同用户可能只能访问特定数据,比如区域销售数据只能区域经理查看。这部分需要在查询时动态添加过滤条件。 技术选型要详细列出每个模块的组件,比如前端用Vue,后端用FastAPI,向量数据库用Milvus,大模型用DeepSeek-67B-chat,NL2SQL用XLS-AMR模型之类的。不过如果时间不,可以用Dify内置的功能来处理问答,减少开发量。 测试方案部分,需要单元测试、集成测试,特别是问答的准确性和数据可视化的正确性。监控日志用ELK或者Prometheus+Grafana。 最后,项目里程碑,分阶段交付,比如第一阶段知识库问答,第二阶段数据可视化,第三阶段整合和优化。 可能还要考虑成本,比如开源组件免费,但部署的服务器成本,大模型的API调用费用。如果自建模型,可能需要GPU资源。 总之,结构要清晰,分模块,技术选型合理,确保可落地。需要详细列出每个部分的技术实现,使用哪些工具,如何整合,确保方案可行。</think># AI智能小助手技术解决方案文档(完整落地版) ## 一、系统架构设计 ``` 用户端 │ ▼ [Web/API接入层] → 身份认证 & 请求路由 │ ▼ [AI核心处理层] ├── 知识库问答模块 │ ├── Milvus向量数据库 │ └── DeepSeek-67B微调模型 └── 数据统计模块 ├── Apache Superset └── SQL生成引擎 │ ▼ [数据存储层] ├── PostgreSQL(业务数据) └── MinIO(文档存储) ``` ## 二、核心模块技术实现 ### 模块1:知识库问答系统 **技术选型:** - 知识库管理:Dify(开源LLMOps平台) - 语义检索:HuggingFace BGE-Large中文embedding模型 - 向量数据库:Milvus 2.3.x - 大模型:DeepSeek-R1-67B-chat(千亿参数级) **实现流程:** 1. 文档预处理流水线: - 格式转换(PDF/Word→Markdown) - 智能分块(采用TextSpliter递归分割) - 元数据注入(条款编号/生效日期等) 2. 检索增强生成(RAG): ```python def rag_pipeline(query): # 混合检索策略 results = hybrid_search( query, vector_weight=0.7, keyword_weight=0.3 ) # 上下文压缩 context = rerank_with_CohereReranker(results) # 生成阶段 return deepseek.generate( f"基于以下条款:{context},回答:{query}", temperature=0.2 ) ``` ### 模块2:数据可视化引擎 **技术栈:** - NL2SQL:Chat2Query(Apache基金会开源方案) - 可视化:Superset + ECharts - 缓存:Redis(查询结果缓存) **自然语言转SQL流程:** ``` 用户问句 → 语义解析 → 数据库schema绑定 → SQL校验 → 执行 → 可视化 ``` **示例处理:** ```sql /* 原始问句:"显示东南区Q3销售额前三的产品" */ SELECT product_name, SUM(amount) FROM sales WHERE region=&#39;东南&#39; AND quarter=3 GROUP BY product_name ORDER BY SUM(amount) DESC LIMIT 3; ``` ## 三、关键技术实现 ### 1. 混合检索策略 $$ \text{Relevance Score} = 0.7 \times \text{CosineSimilarity} + 0.3 \times \text{BM25} $$ ### 2. 安全控制方案 - 基于RBAC的权限管理 - 数据脱敏处理: ```python def desensitize(text): # 自动识别并替换敏感信息 return re.sub(r&#39;\b\d{4}-\d{8}\b&#39;, &#39;****&#39;, text) ``` ### 3. 性能优化措施 - 异步处理架构(Celery + RabbitMQ) - 分级缓存策略: ``` 内存缓存(高频问题)→ Redis缓存(SQL结果)→ 磁盘缓存(文档块) ``` ## 四、部署方案 ### 基础设施要求: | 组件 | 配置要求 | 数量 | |-------------|----------------------|------| | 应用服务器 | 16核64GB(GPU V100) | 2 | | 向量数据库 | 32核128GB | 3节点集群 | | 业务数据库 | SSD云数据库 | 主从架构 | ### 容器化部署: ```docker-compose version: &#39;3.8&#39; services: dify: image: langgenius/dify:latest ports: - "3000:3000" superset: image: apache/superset:2.1 ports: - "8088:8088" ``` ## 五、测试验证方案 ### 测试用例示例: | 测试类型 | 输入样例 | 预期输出 | |---------|---------|---------| | 条款查询 | "员工报销标准" | 返回最新版制度文件段落 | | 数据查询 | "华东区9月销售额趋势" | 生成折线图+TOP3分析 | | 混合查询 | "根据采购条例,2023年电子产品采购金额" | 条款引用+数据图表 | ## 六、项目里程碑 **第一阶段(1-2周)** ✅ 完成知识库文档自动化处理流水线 ✅ 部署测试版问答引擎 **第二阶段(3-4周)** ✅ 实现NL2SQL转换引擎 ✅ 集成数据可视化看板 **第三阶段(5-6周)** ✅ 全链路压力测试(500+并发验证) ✅ 上线试运行+持续监控 --- **备注**:本方案采用模块化设计,可根据实际需求灵活调整组件: - 如需快速上线,可用Supersonic替换Dify - 轻量化部署可选Qwen-72B模型 - 数据安全要求高时可切换为私有化部署的DB-GPT方案
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值