作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
根据Michael Feathers的说法,任何没有测试的代码都被称为遗留代码. 因此,避免创建遗留代码的最佳方法之一是使用测试驱动开发(TDD)。.
虽然有许多工具可用于JavaScript和 反应.js 单元测试,在这篇文章中,我们将使用开玩笑和酶来创建反应.js组件的基本功能使用TDD.
TDD为您的代码带来了许多好处——高测试覆盖率的一个好处是,它使代码重构变得容易,同时保持代码的干净和功能性.
如果你已经创建了一个反应.Js组件之前,您已经意识到代码可以快速增长. 它充满了由与状态更改和服务调用相关的语句引起的许多复杂条件.
每个缺少单元测试的组件都有难以维护的遗留代码. 我们可以 添加单元测试 在我们创建产品代码之后. 然而,我们可能会冒着忽视一些应该被测试的场景的风险. 通过先创建测试, 我们有更高的机会覆盖组件中的每个逻辑场景, 哪一种方法更容易重构和维护.
我们可以使用许多策略来测试反应.js组件:
道具
在分派某个事件时调用.渲染
函数给出当前组件的状态,并将其与预定义的布局相匹配.为了使用这些策略, 我们将使用两个方便的工具来处理反应中的测试.js: 开玩笑 和 酶.
开玩笑是一个由Facebook创建的开源测试框架,它与反应有很好的集成.js. 它包括一个用于测试执行的命令行工具,类似于Jasmine和Mocha提供的工具. 它还允许我们创建几乎没有配置的模拟函数,并提供了一组非常好的匹配器,使断言更容易阅读.
此外, 它提供了一个非常好的功能,叫做“快照测试”,,这有助于我们检查和验证组件渲染结果. 我们将使用快照测试来捕获组件的树,并将其保存到一个文件中,以便与渲染树(或传递给组件的任何树)进行比较 预计
函数作为第一个参数.)
酶提供了一个安装和遍历反应的机制.Js组件树. 这将帮助我们访问它自己的属性和状态,以及它的子道具,以便运行我们的断言.
酶提供组件安装的两个基本功能: 浅
和 山
. 的 浅
函数只在内存中加载根组件,而 山
加载完整的DOM树.
我们将结合酶和开玩笑来安装反应.Js组件,并在其上运行断言.
你可以看一下 这种回购,它具有运行此示例所需的基本配置.
我们使用以下版本:
{
“反应”:“16.0.0",
“酶”:“^ 2.9.1",
“笑话”:“^ 21.2.1",
:“jest-cli ^ 21.2.1",
:“babel-jest ^ 21.2.0"
}
第一步是创建一个失败的测试,它将尝试渲染反应.使用酶的浅函数的组件.
MyComponent / /添加.测试.js
从“反应”中导入反应;
从'enzyme'中导入{浅};
导入MyComponent.MyComponent /添加';
describe("MyComponent", () => {
it("should 渲染 my 组件", () => {
const wrapper = 浅( );
});
});
运行测试后,我们得到以下错误:
ReferenceError: MyComponent没有定义.
然后,我们创建提供基本语法的组件,以使测试通过.
MyComponent / /添加.js
从“反应”中导入反应;
导出默认类MyComponent扩展反应.组件{
呈现(){
return ;
}
}
在下一步中,我们将确保我们的组件呈现预定义的UI布局,使用 toMatchSnapshot
来自开玩笑的函数.
调用此方法后,开玩笑自动创建一个名为 (测试FileName).提前
,其中添加了 __提前shots__
文件夹.
这个文件代表了我们期望从组件呈现中得到的UI布局.
然而,鉴于我们正在努力做 纯 在TDD中,我们应该先创建这个文件,然后调用 toMatchSnapshot
函数使测试失败.
这听起来可能有点令人困惑, 因为我们不知道开玩笑使用哪种格式来表示这个布局.
你可能会想要执行 toMatchSnapshot
函数,然后在快照文件中查看结果,这是一个有效的选项. 然而,如果我们真的想使用 纯 在TDD中,我们需要了解快照文件的结构.
快照文件包含与测试名称匹配的布局. 这意味着如果我们的测试有这样的形式:
desc("ComponentA" () => {
it("should do something", () => {
…
}
});
我们应该在exports部分中指定: 组件A应该做一些1
.
您可以阅读更多关于快照测试的内容 在这里.
我们首先创建 MyComponent.测试.js.提前
文件.
/ / __提前shots__ MyComponent /添加.测试.js.提前
exports[' MyComponent应该渲染初始布局1 ']= '
数组(
,
]
`;
然后,我们创建单元测试,检查快照是否与组件子元素匹配.
MyComponent / /添加.测试.js
...
it("should 渲染 initial layout", () => {
/ /当
const 组件 = 浅( );
/ /然后
期望(组件.getElements ()).toMatchSnapshot ();
});
...
我们可以考虑 组件.getElements
作为渲染方法的结果.
将这些元素传递给 预计
方法,以便针对快照文件运行验证.
执行测试后,我们得到以下错误:
接收值与存储快照1不匹配.
预期:
-数组[
MyComponent / /添加.js
从“反应”中导入反应;
导出默认类MyComponent扩展反应.组件{
呈现(){
return ;
}
}
下一步是添加功能 input
通过在函数值改变时执行函数. 方法中指定一个函数 onChange
道具.
我们首先需要更改快照以使测试失败.
/ / __提前shots__ MyComponent /添加.测试.js.提前
exports[' MyComponent应该渲染初始布局1 ']= '
数组(
,
]
`;
首先修改快照的一个缺点是,道具(或属性)的顺序很重要.
中收到的道具将按字母顺序排序 预计
功能,然后根据快照进行验证. 所以,我们应该按这个顺序指定它们.
执行测试后,我们得到以下错误:
接收值与存储快照1不匹配.
预期:
-数组[
onChange ={[功能]}
MyComponent / /添加.js
从“反应”中导入反应;
导出默认类MyComponent扩展反应.组件{
呈现(){
return {}}
type = " text " />;
}
}
然后,我们确保组件的状态在 onChange
事件被分派.
为此,我们创建一个新的单元测试,它将调用 onChange
函数在输入中传递 事件 为了在UI中模拟真实事件.
然后,我们验证组件 状态 包含一个名为 input
.
MyComponent / /添加.测试.js
...
it("should create an entry in 组件 状态", () => {
/ /给定
const 组件 = 浅( );
Const form = 组件.找到(“输入”);
/ /当
form.道具().onChange({目标:{
名称:“名字”,
价值:“括号”
}});
/ /然后
期望(组件.状态(“输入”)).toBeDefined ();
});
我们现在得到以下错误.
要定义的期望值,而不是未定义的接收值
这表明该组件在被调用的状态中没有属性 input
.
我们通过在组件的状态中设置这个条目来使测试通过.
MyComponent / /添加.js
从“反应”中导入反应;
导出默认类MyComponent扩展反应.组件{
呈现(){
return {这.设置状态({输入:"}}}
type = " text " />;
}
}
然后,我们需要确保在新的状态条目中设置了一个值. 我们将从事件中获得这个值.
因此,让我们创建一个测试来确保状态包含这个值.
MyComponent / /添加.测试.js
...
it("should create an entry in 组件 状态 with the 事件 价值", () => {
/ /给定
const 组件 = 浅( );
Const form = 组件.找到(“输入”);
/ /当
form.道具().onChange({目标:{
名称:“名字”,
价值:“括号”
}});
/ /然后
期望(组件.状态(“输入”)).toEqual(“括号”);
});
~~~
不出所料,我们得到了以下错误.
~~
期望值等于:"myValue"
收到:“”
最后,我们通过从事件获取值并将其设置为输入值,从而使该测试通过.
MyComponent / /添加.js
从“反应”中导入反应;
导出默认类MyComponent扩展反应.组件{
呈现(){
return {
这.设置状态({输入:事件.目标.值}}}
type = " text " />;
}
}
在确保所有测试都通过之后,我们可以重构代码.
我们可以提取传递的函数 onChange
属性转换为被调用的新函数 updateState
.
MyComponent / /添加.js
从“反应”中导入反应;
导出默认类MyComponent扩展反应.组件{
updateState(事件){
这.设置状态({
输入:事件.目标.价值
});
}
呈现(){
return ;
}
}
现在我们有了一个简单的反应.使用TDD创建的组件.
Summary
在本例中,我们尝试使用 纯 TDD通过遵循每个步骤编写尽可能少的代码来失败和通过测试.
有些步骤似乎是不必要的,我们可能会跳过它们. 然而,每当我们跳过任何步骤时,我们最终都会使用 更少的纯 TDD版本.
使用不那么严格的TDD过程也是有效的,并且可能工作得很好.
我给你的建议是避免跳过任何步骤,如果你觉得困难也不要难过. TDD是一种不容易掌握的技术,但它绝对值得一试.
如果您有兴趣了解更多关于TDD和相关的行为驱动开发(BDD), 读 你的老板不会欣赏TDD 由托普塔勒同事瑞安·威尔科克斯.
测试驱动的开发是基于我们编写测试和生产代码的顺序的软件开发过程. 简而言之, 我们希望将失败所必需的测试代码和通过所必需的生产代码保持在最低限度.
反应组件结合了逻辑和表示代码. 它主要用于为基于状态和属性变化的web和移动UI组件提供一个抽象层
在过去十年中, Alonso的Oracle认证和全栈工作最近转向了QA自动化和尖锐的BDD解决方案.
世界级的文章,每周发一次.
世界级的文章,每周发一次.