第 3 篇中,我们为
Counter
组件编写了测试,并看到了醒目的**失败(FAIL)**提示。
这就像一枚指南针,告诉我们下一步该做什么。
今天进入
Green
阶段,把红灯变成绿灯。
代码暂时有些粗糙也没关系,先专注于让测试通过。
运行测试时,遇到的第一个错误是找不到组件文件。
先创建文件,让测试能够找到它。
创建 src/components/Counter.tsx 文件。
src/components/Counter.tsxexport default function Counter() {
return (
<div>
{/* 아직은 아무 내용도 없는 빈 껍데기입니다 */}
</div>
);
}
现在回到测试文件(Counter.test.tsx),先前标红的 import 语句就能被正常识别了。
TestingLibraryElementError: Unable to find an element with the text: /현재 숫자: 0/
测试代码在寻找“현재 숫자: 0”这段文字和“+”按钮,但我们刚创建的组件还是空的。
功能稍后再考虑,先把界面上可见的内容直接写死。
src/components/Counter.tsxexport default function Counter() {
return (
<div>
<p>현재 숫자: 0</p>
<button type="button">+</button>
</div>
);
}
仅凭这些代码,就能通过一半的测试。
RTL
会检查界面上是否存在“현재 숫자: 0”文本和“+”按钮,这两项现在都满足了。
现在只剩最后一道关。
测试期望点击按钮后,数字变成
1
。
但目前它固定显示
0
。
为此,我们需要使用
React
的状态管理工具
useState
(Hook)。
因为界面要随用户的点击操作而变化。
src/components/Counter.tsximport { useState } from 'react';
export default function Counter() {
// [상태] 숫자를 기억하는 메모리 상자(state)를 만듭니다. 초기값은 0입니다.
const [count, setCount] = useState(0);
const handleClick = () => {
// [동작] 기존 값에 1을 더해줍니다.
setCount(prev => prev + 1);
};
return (
<div>
{/* 고정된 숫자 0 대신, 변하는 값 count를 보여줍니다. */}
<p>현재 숫자: {count}</p>
{/* 버튼을 누르면 handleClick 함수가 실행됩니다. */}
<button type="button" onClick={handleClick}>
+
</button>
</div>
);
}
现在实现已经完成。
接下来确认测试是否通过。
打开终端并输入命令。
Terminal

查看测试结果
PASS src/components/Counter.test.tsx
Counter 컴포넌트 동작 테스트
✓ 초기값은 0이며, + 버튼을 누르면 숫자가 1 증가한다 (50 ms)
看到绿色的
PASS
就成功了!
我们刚刚完成了 TDD 循环中的 **Red(失败)→ Green(通过)**阶段。
再来看看我们写的代码为什么通过了测试。
初始状态
useState(0) 让界面渲染“현재 숫자: 0”,getByText 找到了这段文字。
按钮存在
界面上有 <button>+<button> 标签,因此 getByRole('button', { name: '+' }) 成功找到了按钮。
点击行为
执行 user.click() 时触发 onClick 事件,继而调用 setCount。
验证结果
状态变为 1 后,界面重新渲染,测试找到了“현재 숫자: 1”这段文字。
Terminalgit clone https://github.com/PROMLEE/my-tdd-app.git
cd my-tdd-app
git checkout part4
今天实现了组件,让失败的测试通过。
最小实现
从测试要求的元素(文本和按钮)开始,不必一开始就把所有细节做完。
状态管理
只在值需要变化的地方引入 useState
,完成动态交互。
信心
测试通过说明这个组件满足了当前的基本要求。
目前的代码很简单,似乎没有太多需要改进的地方。
如果组件变得更复杂,又该怎么办?
第 5 篇将介绍
组件重构与测试维护
,看看如何在已有测试的情况下整理代码。