<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ 测试驱动开发 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ 测试驱动开发 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 11 May 2026 15:33:09 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/tdd/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 测试驱动开发指南——如何测试你的 JavaScript 和 ReactJS 应用程序 ]]>
                </title>
                <description>
                    <![CDATA[ 想要成为高产的软件开发工程师，了解测试驱动的开发必不可少。测试是创建可靠程序的基石。 这篇教程会帮助你在JavaScript和React应用中实现测试驱动的开发。 目录  1.  什么是测试驱动开发  2.  测试驱动开发工作流的JavaScript示例  3.  如何使用Jest来测试执行  4.  在Jest中使用es6模块须知  5.  测试驱动的开发有什么好处  6.  测试驱动开发中的单元测试是什么  7.  测试驱动开发中的集成测试是什么  8.  测试驱动开发中的端到端测试是什么  9.  测试驱动开发中的测试替身是什么  10. 阶段性总结测试驱动开发  11. 如何测试React组件  12. 测试运行工具vsReact组件测试工具：区别是什么  13. ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app/</link>
                <guid isPermaLink="false">6304919360480505ded7af2a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 测试驱动开发 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ PapayaHUANG ]]>
                </dc:creator>
                <pubDate>Tue, 23 Aug 2022 08:44:54 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/08/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app-codesweetly-battlecreek-coffee-roasters-i22gbC3gFm4-unsplash.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Test-Driven Development Tutorial – How to Test Your JavaScript and ReactJS Applications</a>
      </p><!--kg-card-begin: markdown--><p>想要成为高产的软件开发工程师，了解测试驱动的开发必不可少。测试是创建可靠程序的基石。</p>
<p>这篇教程会帮助你在JavaScript和React应用中实现测试驱动的开发。</p>
<h2 id="">目录</h2>
<ol>
<li><a href="#what-is-test-driven-development">什么是测试驱动开发</a></li>
<li><a href="#javascript-example-of-a-test-driven-development-workflow">测试驱动开发工作流的JavaScript示例</a></li>
<li><a href="#how-to-use-jest-as-a-test-implementation-tool">如何使用Jest来测试执行</a></li>
<li><a href="#important-stuff-to-know-about-using-es6-modules-with-jest">在Jest中使用es6模块须知</a></li>
<li><a href="#what-are-the-advantages-of-test-driven-development">测试驱动的开发有什么好处</a></li>
<li><a href="#what-is-a-unit-test-in-test-driven-development">测试驱动开发中的单元测试是什么</a></li>
<li><a href="#what-is-an-integration-test-in-test-driven-development">测试驱动开发中的集成测试是什么</a></li>
<li><a href="#what-is-an-end-to-end-test-in-test-driven-development">测试驱动开发中的端到端测试是什么</a></li>
<li><a href="#what-are-test-doubles-in-test-driven-development">测试驱动开发中的测试替身是什么</a></li>
<li><a href="#quick-overview-of-test-driven-development-so-far">阶段性总结测试驱动开发</a></li>
<li><a href="#how-to-test-react-components">如何测试React组件</a></li>
<li><a href="#test-runner-vs-react-component-testing-tool-what-s-the-difference">测试运行工具vsReact组件测试工具：区别是什么</a></li>
<li><a href="#project-how-react-testing-works">项目：React测试如何运行</a></li>
<li><a href="#overview">总结</a></li>
</ol>
<p>话不多说，让我们开始从了解什么是测试驱动开发开始吧！</p>
<h2 id="what-is-test-driven-development">什么是测试驱动开发</h2>
<p><strong>测试驱动开发（TDD）</strong> 是一种编程实践，你先写出你预期的程序会产生的结果，再编写程序。</p>
<p>也就是说，TDD需要你预先构思好程序的输出，来通过你展望想实现的功能的测试。</p>
<p>所以，一种高效实践TDD的方法是你首先编写测试你预期结果的程序。</p>
<p>然后，你创建可以通过测试的程序。</p>
<p>举个例子，假设你想要创建一个加法计算器，TDD方法如图：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/07/test-driven-development-tdd-workflow-diagram-codesweetly.png" alt="测试驱动开发工作流示意图" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>测试驱动开发工作流示意图</figcaption>
</figure>
<ol>
<li>编写一个测试，指定你希望计算器产生的结果</li>
<li>开发计算器，然后通过预先写好的测试</li>
<li>执行测试，检查计算器是否通过</li>
<li>重构测试代码（如有必要）</li>
<li>重构程序（如有必要）</li>
<li>重复循环，直至计算器符合你的预期</li>
</ol>
<p>让我们来看一个用JavaScript实现的例子</p>
<h2 id="javascript-example-of-a-test-driven-development-workflow">测试驱动开发工作流的JavaScript示例</h2>
<p>让我们用一个简单的JavaScript程序，来分步骤实现测试驱动编程的工作流：</p>
<h3 id="1">1. 编写测试</h3>
<p>编写一个测试，指定计算器的输出：</p>
<pre><code class="language-js">function additionCalculatorTester() {
  if (additionCalculator(4, 6) === 10) {
    console.log("✔ Test Passed");
  } else {
    console.error("❌ Test Failed");
  }
}
</code></pre>
<h3 id="2">2. 开发程序</h3>
<p>编写一个计算器程序以通过编写好的测试</p>
<pre><code class="language-js">function additionCalculator(a, b) {
  return a + b;
}
</code></pre>
<h3 id="3">3. 执行测试</h3>
<p>执行测试，查看程序是否通过测试</p>
<pre><code class="language-js">additionCalculatorTester();
</code></pre>
<p><a href="https://stackblitz.com/edit/js-ciui1u?devToolsHeight=33&amp;file=index.js"><strong>在StackBlitz查看示例</strong></a></p>
<h3 id="4">4. 重构测试</h3>
<p>在确认程序通过测试之后，可以检查是否需要重构测试代码。</p>
<p>例如，你可以使用<a href="https://codesweetly.com/javascript-statement/#what-is-a-conditional-ternary-operator-in-javascript">三元运算符</a>来重构<code>additionCalculatorTester()</code>：</p>
<pre><code class="language-js">function additionCalculatorTester() {
  additionCalculator(4, 6) === 10 
    ? console.log("✔ Test Passed") 
    : console.error("❌ Test Failed");
}
</code></pre>
<h3 id="5">5. 重构程序</h3>
<p>让我们使用<a href="https://codesweetly.com/javascript-function-object#arrow-function-expression-in-javascript">箭头函数</a>来重构程序：</p>
<pre><code class="language-js">const additionCalculator = (a, b) =&gt; a + b;
</code></pre>
<h3 id="6">6. 执行测试</h3>
<p>重新执行测试，确保程序仍然能够通过测试</p>
<pre><code class="language-js">additionCalculatorTester();
</code></pre>
<p><a href="https://stackblitz.com/edit/js-xp732h?devToolsHeight=33&amp;file=index.js"><strong>在StackBlitz查看示例</strong></a></p>
<p>注意在以上例子中，我们没有使用任何第三方库。</p>
<p>其实你可以使用强大的第三方库来执行测试，如：<a href="https://jasmine.github.io/">Jasmine</a>、 <a href="https://mochajs.org/">Mocha</a>、 <a href="https://github.com/substack/tape">Tape</a>和<a href="https://jestjs.io/">Jest</a>，这些库可以使你的测试运行得更加快速、简洁并充满乐趣。</p>
<p>让我们一起看看如何使用Jest。</p>
<h2 id="#how-to-use-jest-as-a-test-implementation-tool">如何使用Jest来测试执行</h2>
<p>在使用Jest工具之前，你需要执行以下步骤：</p>
<h3 id="nodenpm">第一步：使用正确的Node和NPM版本</h3>
<p>确保你的系统上装有Node 10.16（或者更高版本）和 NPM 5.6（或者更高版本）。</p>
<p>你可以在<a href="https://nodejs.org/en/">Node.js</a>官网下载最新的LTS。</p>
<p>如果你更倾向于使用Yarn，确保你使用<a href="https://yarnpkg.com/">Yarn 0.25 (或者更高版本)</a>。</p>
<h3 id="">第二步： 创建一个项目目录</h3>
<p>为你的项目创建一个目录</p>
<pre><code class="language-bash">mkdir addition-calculator-jest-project
</code></pre>
<h3 id="">第三步：导航到你的项目文件夹</h3>
<p>使用命令行导航到你的项目文件夹</p>
<pre><code class="language-bash">cd path/to/addition-calculator-jest-project
</code></pre>
<h3 id="packagejson">第四步：创建一个<code>package.json</code>文件</h3>
<p>在项目中初始化 <code>package.json</code> 文件</p>
<pre><code class="language-bash">npm init -y
</code></pre>
<p>如果你的<a href="https://codesweetly.com/package-manager-explained">包管理器</a>是Yarn，执行：</p>
<pre><code class="language-bash">yarn init -y
</code></pre>
<h3 id="jest">第五步：安装Jest</h3>
<p>把Jest作为开发依赖包安装</p>
<pre><code class="language-bash">npm install jest --save-dev
</code></pre>
<p>如果你使用的是Yarn，执行：</p>
<pre><code class="language-bash">yarn add jest --dev
</code></pre>
<h3 id="jest">第六步：设置Jest为项目测试运行工具</h3>
<p>打开<code>package.json</code>文件，并把Jest添加到<code>test</code>区域。</p>
<pre><code class="language-json">{
  "scripts": {
    "test": "jest"
  }
}
</code></pre>
<h3 id="">第七步：创建项目文件</h3>
<p>创建一个文件，在这个文件里编写开发代码</p>
<pre><code class="language-bash">touch additionCalculator.js
</code></pre>
<h3 id="">第八步：创建测试文件</h3>
<p>创建一个编写测试案例的文件</p>
<pre><code class="language-bash">touch additionCalculator.test.js
</code></pre>
<p><strong>注意：</strong> 测试文件的结尾必须是 <code>.test.js</code>，这样Jest才能够分辨出来这个文件是测试文件。</p>
<h3 id="">第九步：编写测试案例</h3>
<p>打开测试文件，编写你希望程序产出的指定结果。</p>
<p><strong>例子:</strong></p>
<pre><code class="language-js">// additionCalculator.test.js

const additionCalculator = require("./additionCalculator");

test("addition of 4 and 6 to equal 10", () =&gt; {
  expect(additionCalculator(4, 6)).toBe(10);
});
</code></pre>
<p>在上述代码块中：</p>
<ol>
<li>我们将<code>additionCalculator.js</code>项目文件导入到 <code>additionCalculator.test.js</code>测试文件。</li>
<li>我们编写了一个测试案例，希望当用户提供的<a href="https://codesweetly.com/javascript-arguments">参数</a>是<code>4</code>和<code>6</code>的时候，<code>additionCalculator()</code>程序的输出是 <code>10</code>。</li>
</ol>
<p><strong>注意：</strong></p>
<ul>
<li><a href="https://jestjs.io/docs/api#testname-fn-timeout"><code>test()</code></a>是Jest的全局方法，接受三个参数：
<ol>
<li>测试名 (<code>"addition of 4 and 6 to equal 10"</code>)</li>
<li>一个包含你期望测试结果的函数</li>
<li>一个可选的timeout参数</li>
</ol>
</li>
<li><a href="https://jestjs.io/docs/expect#expectvalue"><code>expect()</code></a>是一个测试代码输出的Jest方法。</li>
<li><a href="https://jestjs.io/docs/expect#tobevalue"><code>toBe()</code></a> 是一个<a href="https://jestjs.io/docs/using-matchers">Jest匹配器</a>函数，可以对比 <code>expect()</code>参数和原始值。</li>
</ul>
<p>假设你现在执行这个测试，测试将不会通过，因为你还没有编写程序，让我们现在开始吧！</p>
<h3 id="">第十步：开发程序</h3>
<p>打开项目文件，开发可以通过测试的程序。</p>
<p><strong>这里是例子:</strong></p>
<pre><code class="language-js">// additionCalculator.js

function additionCalculator(a, b) {
  return a + b;
}

module.exports = additionCalculator;
</code></pre>
<p>上面的代码块创建了一个<code>additionCalculator()</code>程序，并且使用<code>module.exports</code>方法将程序导出。</p>
<h3 id="">第十一步：执行测试</h3>
<p>执行测试，查看程序是否通过：</p>
<pre><code class="language-bash">npm run test
</code></pre>
<p>也可以使用Yarn：</p>
<pre><code class="language-bash">yarn test
</code></pre>
<p>假设你的项目有多个测试文件，你想执行其中一个，你可以通过以下代码实现：</p>
<pre><code class="language-bash">npm run test additionCalculator.test.js
</code></pre>
<p>如果使用Yarn的话是这样：</p>
<pre><code class="language-bash">yarn test additionCalculator.test.js
</code></pre>
<p>一旦你启动了测试，Jest会在你的编辑器控制台打印出通过或者不通过的消息，消息如下：</p>
<pre><code class="language-bash">$ jest
 PASS  ./additionCalculator.test.js
  √ addition of 4 and 6 to equal 10 (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.002 s
Ran all test suites.
Done in 7.80s.
</code></pre>
<p>如果你希望Jest自动执行测试，可以在<code>package.json</code>的<code>test</code>区域添加 <code>--watchAll</code> 选项。</p>
<p><strong>例子:</strong></p>
<pre><code class="language-json">{
  "scripts": {
    "test": "jest --watchAll"
  }
}
</code></pre>
<p>添加<code>--watchAll</code>后，重新执行<code>npm run test</code> (或 <code>yarn test</code>)命令，Jest会在每次保存后重新执行测试。</p>
<p><strong>注意:</strong> 你可以使用键盘上的<strong>Q</strong>键退出监视（watch）模式。</p>
<h3 id="">第十二步：重构测试代码</h3>
<p>我们已经确认了程序可以如预期执行，是时候来检查是否需要重构测试代码了。</p>
<p>例如，假设你想要<code>additionalCalculator</code>允许用户输入任意数量的数字。你可以这样重构你的代码：</p>
<pre><code class="language-js">// additionCalculator.test.js

const additionCalculator = require("./additionCalculator");

describe("additionCalculator's test cases", () =&gt; {
  test("addition of 4 and 6 to equal 10", () =&gt; {
    expect(additionCalculator(4, 6)).toBe(10);
  });

  test("addition of 100, 50, 20, 45 and 30 to equal 245", () =&gt; {
    expect(additionCalculator(100, 50, 20, 45, 30)).toBe(245);
  });

  test("addition of 7 to equal 7", () =&gt; {
    expect(additionCalculator(7)).toBe(7);
  });

  test("addition of no argument provided to equal 0", () =&gt; {
    expect(additionCalculator()).toBe(0);
  });
});
</code></pre>
<p>注意上面的代码块中的<a href="https://jestjs.io/docs/api#describename-fn">describe()</a>方法，是可选的。这个方法可以帮助将同类型的测试分门别类在一起。</p>
<p><code>describe()</code>接受两个参数：</p>
<ol>
<li>你希望的测试案例组的名字，如： <code>"additionCalculator's test cases"</code>.</li>
<li>包含测试案例的函数</li>
</ol>
<h3 id="">第十三步：重构程序</h3>
<p>在重构了测试代码之后，让我们重构一下<code>additionalCalculator</code>程序。</p>
<pre><code class="language-js">// additionCalculator.js

function additionCalculator(...numbers) {
  return numbers.reduce((sum, item) =&gt; sum + item, 0);
}

module.exports = additionCalculator;
</code></pre>
<p>在代码块中我们做了这些事情：</p>
<ol>
<li><code>...numbers</code>代码使用了JavaScript中的<a href="https://codesweetly.com/javascript-rest-operator">展开操作符</a> (<code>...</code>) ，将函数的参数转化为一个数组。</li>
<li><code>numbers.reduce((sum, item) =&gt; sum + item, 0)</code>代码使用JavaScript的<a href="https://codesweetly.com/javascript-reduce-method">reduce()</a>方法，求和了<code>numbers</code>数组中的所有元素。</li>
</ol>
<h3 id="">第十四步：重新执行测试</h3>
<p>重构程序之后，可以重新执行测试，查看是否通过。</p>
<h3 id="">结束</h3>
<p>恭喜你！你成功地使用Jest，并借助测试驱动开发的方法创建了一个计算器程序。 🎉</p>
<h2 id="important-stuff-to-know-about-using-es6-modules-with-jest">在Jest中使用es6模块须知</h2>
<p>目前，Jest不能识别ES6模块。</p>
<p>假设，你习惯使用ES6的import/export声明，你必须采取以下步骤：</p>
<h3 id="1babel">1. 安装Babel作为依赖包</h3>
<pre><code class="language-bash">npm install @babel/preset-env --save-dev
</code></pre>
<p>或者使用Yarn：</p>
<pre><code class="language-bash">yarn add @babel/preset-env --dev
</code></pre>
<h3 id="2rootbabelrc">2. 在项目的root创建 <code>.babelrc</code>文件：</h3>
<pre><code class="language-bash">touch .babelrc
</code></pre>
<h3 id="3babelrc">3. 打开 <code>.babelrc</code> 文件，并且复制以下代码：</h3>
<pre><code class="language-json">{ "presets": ["@babel/preset-env"] }
</code></pre>
<p>这样设置之后，上一章节第九步的 <code>require()</code>声明，可以从</p>
<pre><code class="language-js">const additionCalculator = require("./additionCalculator");
</code></pre>
<p>...变成：</p>
<pre><code class="language-js">import additionCalculator from "./additionCalculator";
</code></pre>
<p>同样的，你也可以替换掉第十步的<code>export</code>声明，从</p>
<pre><code class="language-js">module.exports = additionCalculator;
</code></pre>
<p>到：</p>
<pre><code class="language-js">export default additionCalculator;
</code></pre>
<p><strong>注意：</strong> Jest在<a href="https://jestjs.io/docs/getting-started#using-babel">使用Babel</a>文档中，指定了类似说明。</p>
<h3 id="4">4. 重新执行测试</h3>
<p>你可以重新执行测试，确保程序仍然通过测试。</p>
<p>现在你已经知道测试驱动的开发是什么，让我们来看看这一方法有什么好处。</p>
<h2 id="what-are-the-advantages-of-test-driven-development">测试驱动的开发有什么好处？</h2>
<p>在你的开发工作流中引入测试驱动开发（TDD）有以下两大好处：</p>
<h3 id="1">1. 理解程序的目的</h3>
<p>测试驱动的开发可以帮助你理解程序的目的。</p>
<p>也就是说，因为你在编写实际的程序前已经编写了测试，所以TDD可以促使你去思考你想要程序做什么事。</p>
<p>在你通过一到两个测试记录下来你的程序的目的之后，你可以自信地去创建程序。</p>
<p>因此，TDD可以有效地帮助你记录下来你希望程序产生的结果。</p>
<h3 id="2">2. 信心助推器</h3>
<p>TDD是了解你的程序是否如预期工作的的一个基准。它给予你信心，相信自己的程序正确执行。</p>
<p>所以无论之后你的代码库会有什么变化，TDD都可以有效地确保你的程序能够执行。</p>
<p>让我们现在来讨论一下TDD的术语： "单元测试（unit test)"、 "集成测试(integration test)"、 "端对端（E2E）"、和 "测试替身(test doubles)"。</p>
<h2 id="what-is-a-unit-test-in-test-driven-development">测试驱动开发中的单元测试是什么</h2>
<p><strong>单元测试</strong>是用于评估程序独立功能的测试。换句话说，单元测试检查一个完全独立的程序单元是不是按照预期工作。</p>
<p>我们为<code>additionalCalculator</code>程序编写的第十步里的测试就是一个完美的例子。</p>
<p>第十步里的<code>additionalCalculator()</code>测试是一个独立的函数，不依赖任何外部代码。</p>
<p>注意单元测试首要目的并不是检查是否有bug，而是检查程序的一个独立片段（被称作单元）是否在不同的情况下按照预期工作。</p>
<h2 id="what-is-an-integration-test-in-test-driven-development">测试驱动开发中的集成测试是什么？</h2>
<p><strong>集成测试</strong>评估依赖程序的功能。也就是说，集成测试检查一个程序（依赖其他代码）是不是按照要求工作。</p>
<p>我们为 <code>additionalCalculator</code>程序编写的第十三步的测试就是一个很好的例子。</p>
<p>第十三步的<code>additionalCalculator()</code>的测试一个例子是因为这个程序是一个依赖函数，依赖了JavaScript的<a href="https://codesweetly.com/javascript-reduce-method">reduce()</a>方法。</p>
<p>也就是说，我们使用事先编写好的测试案例来测试 <code>additionalCalculator()</code>和<code>reduce()</code>。</p>
<p>因此，如果JavaScript把<code>reduce()</code>规定为一个过时的方法，那么在这个案例中，<code>additionalCalculator</code>会因为<code>reduce()</code>方法而无法通过测试。</p>
<h2 id="what-is-an-end-to-end-test-in-test-driven-development">测试驱动开发中的端到端测试是什么？</h2>
<p><strong>端到端(E2E)测试</strong>访问用户接口（UI）的功能，也就是说E2E检查UI是否按照意图工作。</p>
<p>可以观看<a href="https://youtu.be/r9HdJ8P6GQI?t=1755">Max的Youtube频道</a>了解更多。</p>
<h2 id="what-are-test-doubles-in-test-driven-development">测试驱动开发中的测试替身是什么？</h2>
<p>**测试替身（test doubles）**是模仿对象，用于模仿如数据库、库、网络和API等真实的依赖项。</p>
<p>使用测试替身可以绕过程序真实的依赖对象，你可以独立于任何依赖项来测试你的代码。</p>
<p>假设你需要测试应用的一个错误是由外部API还是你自己的代码引起的。</p>
<p>但这个API仅在生产阶段，而不在开发阶段提供服务。所以，你有两种选择：</p>
<ol>
<li>一直等到应用投入使用（这可能要等上数月）；</li>
<li>克隆API，这样不论这个依赖项是否可用，你都可以继续测试。</li>
</ol>
<p>使用测试替身来克隆项目依赖项，能够帮助你在不打断进度的情况下进行应用测试。</p>
<p>测试替身的典型示例是虚拟对象（dummy）、模拟（mock）、桩（stub）和仿冒（fake）。</p>
<h3 id="dummy">测试驱动开发中的虚拟对象（dummy）是什么</h3>
<p><strong>虚拟对象（dummy）</strong> 是用于模仿特定依赖项的值的测试替身。</p>
<p>假设你的应用依赖一个第三方的方法来提供一些参数。虚拟对象可以传入虚假的值给需要的方法提供参数。</p>
<h3 id="mock">测试驱动开发中的模拟（mock）是什么</h3>
<p><strong>模拟（mock）</strong> 是用于模仿外部依赖项的测试替身，使用模拟可以在开发的过程中不考虑依赖项的返回。</p>
<p>假设你的应用依赖第三方API（如：Facebook），而这个API不可以在开发模式中被访问。使用模拟可以绕过这个API，这样你可以在不考虑Facebook的API是否可以访问的情况下进行测试。</p>
<h3 id="stub">测试驱动开发中的桩（stub）是什么</h3>
<p><strong>桩（stub）</strong> 使用手动输入的值来模仿外部依赖项的返回值。你可以使用不同的返回值来测试应用的性能。</p>
<p>假设你的应用依赖于第三方API（如：Facebook），而这个API不可以在开发模式中被访问。桩模仿Facebook的返回值让你可以绕开这个API做测试。</p>
<p>因此，桩可以帮助你获取不同响应场景的应用行为。</p>
<h3 id="fake">测试驱动开发中的仿冒（fake）是什么</h3>
<p><strong>仿冒（fake）</strong> 是用于创建有动态值的外部依赖项的测试替身。</p>
<p>例如你可以使用仿冒来创建一个本地数据库，来测试你的程序如何和实际数据库一起协同工作的。</p>
<h2 id="quick-overview-of-test-driven-development-so-far">阶段性总结测试驱动开发</h2>
<p>我们学习了测试驱动开发如何在创建程序前记录程序的行为。</p>
<p>我们也实践了一个简单的JavaScript测试，并且使用Jest来作为测试的工具。</p>
<p>现在让我们一起来看看如何测试React组件。</p>
<h2 id="how-to-test-react-components">如何测试React组件</h2>
<p>两个主要的测试React组件的工具是：</p>
<ol>
<li>测试运行工具</li>
<li>React组件测试工具</li>
</ol>
<p>测试运行工具和React组件测试工具的主要区别是什么？</p>
<h2 id="test-runner-vs-react-component-testing-tool-what-s-the-difference">测试运行工具 vs React组件测试工具：区别是什么？</h2>
<p>以下是测试运行工具和React组件测试工具的主要区别：</p>
<h3 id="">什么是测试运行</h3>
<p><strong>测试运行</strong>是一种测试工具，执行测试脚本，并将结果打印在命令行(CLI)。</p>
<p>假设你想要执行你的项目中<code>App.test.js</code>的测试脚本中的测试案例，你就可以使用测试运行。</p>
<p>测试运行执行<code>App.test.js</code>，并将结果打印在命令行。</p>
<p>典型的测试运行工具有：<a href="https://jasmine.github.io/">Jasmine</a>、 <a href="https://mochajs.org/">Mocha</a>、 <a href="https://github.com/substack/tape">Tape</a>和<a href="https://jestjs.io/">Jest</a>。</p>
<h3 id="react">什么是React组件测试工具</h3>
<p><strong>React组件测试工具</strong>提供强大的API来定义组件测试案例。</p>
<p>假设你需要测试你的项目的<code>&lt;App /&gt;</code>组件，你可以使用React组件测试工具来定义组件的测试案例。</p>
<p>也就是说，这个测试工具提供API来编写组件的测试案例。</p>
<p>典型的组件测试工具有： <a href="https://enzymejs.github.io/enzyme/">Enzyme</a> 和 <a href="https://testing-library.com/docs/react-testing-library/intro">React Testing Library</a>。</p>
<p>现在你已经知道了测试运行工具和React组件测试工具是什么，让我们来利用一个简单的项目例子进一步了解React测试是如何运行的。</p>
<h2 id="project-how-react-testing-works">项目：React测试如何运行</h2>
<p>在接下来的例子中，我们将使用<a href="https://en.wikipedia.org/wiki/Jest_(JavaScript_framework)">Jest</a>和<a href="https://testing-library.com/docs/react-testing-library/intro">React Testing Library</a> (文档由Kent C. Dodds编写)来学习React测试是如何运行的。</p>
<p><strong>注意：</strong> React官方文档<a href="https://reactjs.org/docs/testing.html#tools">推荐</a>结合Jest和React Testing Library一起来测试React组件。</p>
<h3 id="nodenpm">第一步：获取正确的Node和NPM版本</h3>
<p>确保你的系统安装的是<a href="https://codesweetly.com/package-manager-explained#how-to-check-the-installed-node-version">Node 10.16</a> (或者更高版本)以及NPM 5.6 (或者更高版本)。</p>
<p>如果你倾向于使用Yarn，确保你安装的是Yarn 0.25 (或者更高版本).</p>
<h3 id="react">第二步：创建一个新的React应用</h3>
<p>使用NPM的<a href="https://create-react-app.dev/">create-react-app</a>包来创建一个名为<code>react-testing-project</code>的项目：</p>
<pre><code class="language-bash">npx create-react-app react-testing-project
</code></pre>
<p>同样，你可以使用Yarn来创建：</p>
<pre><code class="language-bash">yarn create react-app react-testing-project
</code></pre>
<h3 id="">第三步：导航进入项目目录</h3>
<p>创建完毕后，导航进入到项目目录</p>
<pre><code class="language-bash">cd react-testing-project
</code></pre>
<h3 id="">第四步：设置测试环境</h3>
<p>安装下列测试包</p>
<ul>
<li>jest</li>
<li>@testing-library/react</li>
<li>@testing-library/jest-dom</li>
<li>@testing-library/user-event</li>
</ul>
<p><strong>注意:</strong> 如果你是通过<code>create-react-app</code> (第二步)来初始化你的项目，你就不需要安装这些测试包。这些测试包已经被预安装到了<code>package.json</code>文件中。</p>
<p>现在让我们讲解一下这些测试包的作用：</p>
<h4 id="jest">什么是Jest</h4>
<p><a href="https://www.npmjs.com/package/jest">jest</a>是个测试运行工具，我们可以使用这个工具来运行测试脚本，并将结果打印在命令行。</p>
<h4 id="testinglibraryreact">什么是@testing-library/react</h4>
<p><a href="https://www.npmjs.com/package/@testing-library/react">@testing-library/react</a>是一个React测试库，提供为React组件编写测试案例的API。</p>
<h4 id="testinglibraryjestdom">什么是@testing-library/jest-dom</h4>
<p><a href="https://www.npmjs.com/package/@testing-library/jest-dom">@testing-library/jest-dom</a>提供定制的Jest匹配器来测试DOM的状态。</p>
<p><strong>注意:</strong> Jest已经包含很多匹配器，所以使用<code>jest-dom</code>是可选的。 <code>jest-dom</code>只是扩展了Jest匹配器，使得测试更加声明式、易阅读以及更容易维护。</p>
<h4 id="testinglibraryuserevent">什么是@testing-library/user-event</h4>
<p><a href="https://www.npmjs.com/package/@testing-library/user-event">@testing-library/user-event</a>提供<code>userEvent</code>API来模拟在web上用户和应用的交互。</p>
<p><strong>注意:</strong> <code>@testing-library/user-event</code>比<a href="https://testing-library.com/docs/dom-testing-library/api-events/#fireevent">fireEvent</a> API更好用。</p>
<h3 id="src">第五步: 清空<code>src</code>文件夹</h3>
<p>删除所有在<code>src</code>文件夹里的文件。</p>
<h3 id="">第六步: 创建代码文件</h3>
<p>在<code>src</code>文件夹中创建以下文件：</p>
<ul>
<li><code>index.js</code></li>
<li><code>App.js</code></li>
<li><code>App.test.js</code></li>
</ul>
<h3 id="app">第七步：渲染<code>App</code>组件</h3>
<p>打开<code>index.js</code>文件，并在DOM渲染<code>App</code>组件：</p>
<pre><code class="language-js">// index.js

import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

// 在根DOM渲染APP组件
createRoot(document.getElementById("root")).render(&lt;App /&gt;);
</code></pre>
<h3 id="">第八步：创建测试案例</h3>
<p>假设你希望<code>App.js</code>文件在网页渲染一个<code>&lt;h1&gt;CodeSweetly Test&lt;/h1&gt;</code> 元素。打开 <em>test script</em> 并编写你希望 <code>&lt;App /&gt;</code>组件生产的结果。</p>
<p><strong>例子:</strong></p>
<pre><code class="language-js">// App.test.js

import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import App from "./App";

test("codesweetly test heading", () =&gt; {
  render(&lt;App /&gt;);
  expect(screen.getByRole("heading")).toHaveTextContent(/codesweetly test/i);
});
</code></pre>
<p>上面的测试代码片段主要做了这些事：</p>
<ol>
<li>引入了测试案例需要的包</li>
<li>编写了测试案例，希望 <code>&lt;App /&gt;</code>组件可以渲染一个head元素包含 <code>"codesweetly test"</code>文本。</li>
</ol>
<ul>
<li><a href="https://jestjs.io/docs/api#testname-fn-timeout"><code>test()</code></a>是Jest的一个全局方法。我们使用它运行测试案例。这个方法接受三个参数：
<ul>
<li>测试名（<code>"codesweetly test heading"</code>）</li>
<li>包含期望测试结果的函数</li>
<li>可选的timeout参数</li>
</ul>
</li>
<li><a href="https://testing-library.com/docs/react-testing-library/api/#render"><code>render()</code></a>是React Testing library的一个API，我们使用它来渲染我们希望测试的组件。</li>
<li><a href="https://jestjs.io/docs/expect#expectvalue"><code>expect()</code></a>是一个测试代码结果的Jest方法。</li>
<li><a href="https://testing-library.com/docs/queries/about/#screen"><code>screen</code></a>是一个包含多种搜寻页面元素方法的React Testing Library对象。</li>
<li><a href="https://testing-library.com/docs/queries/about/#priority"><code>getByRole()</code></a>是搜寻页面元素的一个React Testing Library的请求方法。</li>
<li><a href="https://github.com/testing-library/jest-dom#tohavetextcontent"><code>toHaveTextContent()</code></a>是 <code>jest-dom</code>的一个定制匹配器，可以使用它来确认特定节点存在文本内容。</li>
<li><code>/codesweetly test/i</code> 是一个<a href="https://codesweetly.com/javascript-regular-expression-object">正则表达式</a> 语法，用于表达搜索不区分大小写的<code>codesweetly test</code>。</li>
</ul>
<p>记住有三种方式来编写上面的声明：</p>
<pre><code class="language-js">// 1. 使用jest-dom'的toHaveTextContent()方法：
expect(screen.getByRole("heading")).toHaveTextContent(/codesweetly test/i);

// 2. 使用头部的textContent属性和Jest的toMatch()方法：
expect(screen.getByRole("heading").textContent).toMatch(/codesweetly test/i);

// 3. 使用React Testing Library的名称选项和jest-dom的toBeInTheDocument()方法：
expect(screen.getByRole("heading", { name: /codesweetly test/i })).toBeInTheDocument();
</code></pre>
<p><strong>提示:</strong></p>
<p>可以添加<code>level</code>选项到<code>getByRole()</code>方法，来标注head的层级。</p>
<p><strong>例子:</strong></p>
<pre><code class="language-js">test("codesweetly test heading", () =&gt; {
  render(&lt;App /&gt;);
  expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(/codesweetly test/i);
});
</code></pre>
<p><code>level: 1</code> 代表了<code>&lt;h1&gt;</code>元素。</p>
<p>假设你现在运行测试，会测试失败，因为还没有编写组件，所以我们现在开始编写：</p>
<h3 id="react">第九步：开发你的React组件</h3>
<p>打开<code>App.js</code>文件来开发一个可以通过测试的组件</p>
<p><strong>例子:</strong></p>
<pre><code class="language-js">// App.js

import React from "react";

const App = () =&gt; &lt;h1&gt;CodeSweetly Test&lt;/h1&gt;;

export default App;
</code></pre>
<p>在代码片段中，<code>App</code>组件渲染了一个<code>&lt;h1&gt;</code>元素包含了 <code>"CodeSweetly Test"</code>文本。</p>
<h3 id="">第十步：执行测试</h3>
<p>执行实现写好的测试，检查测试通过还是失败：</p>
<pre><code class="language-bash">npm test App.test.js
</code></pre>
<p>也可以使用Yarn：</p>
<pre><code class="language-bash">yarn test App.test.js
</code></pre>
<p>初始化测试后，Jest会在你的编辑器的控制台打印通过或者失败的消息：</p>
<pre><code class="language-bash">$ jest
 PASS  src/App.test.js
  √ codesweetly test heading (59 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.146 s
Ran all test suites related to changed files.
</code></pre>
<p><strong>注意:</strong> <code>create-react-app</code>默认在<a href="https://codesweetly.com/javascript-module-bundler/#what-is-webpack---progress---watch">watch mode</a>配置Jest。所以，在执行 <code>npm test</code> (或者<code>yarn test</code>)之后，你当前打开的终端会继续执行<code>test</code>命令的活动。在<code>test</code>执行的过程中，你将没办法在终端输入任何内容，但是你可以同时期开启一个新的终端窗口来执行<code>test</code>。</p>
<p>也就是说，使用一个窗口来执行<code>test</code>，另一个来输入命令。</p>
<h3 id="">第十一步：执行应用</h3>
<p>在浏览器查看应用：</p>
<pre><code class="language-bash">npm start
</code></pre>
<p>如果你的<a href="https://codesweetly.com/package-manager-explained">包管理工具</a> 是Yarn，执行：</p>
<pre><code class="language-bash">yarn start
</code></pre>
<p>一旦执行上述命令，你的默认浏览器就会自动打开你的应用。</p>
<h3 id="">第十二步：重构测试代码</h3>
<p>假设你希望当用户点击按钮的时候改变head的文字。你可以模拟一个按钮来测试这个用户交互是否成立。</p>
<p><strong>例子:</strong></p>
<pre><code class="language-js">// App.test.js

import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import App from "./App";

describe("App component", () =&gt; {
  test("codesweetly test heading", () =&gt; {
    render(&lt;App /&gt;);
    expect(screen.getByRole("heading")).toHaveTextContent(/codesweetly test/i);
  });

  test("a codesweetly project heading", () =&gt; {
    render(&lt;App /&gt;);

    const button = screen.getByRole("button", { name: "Update Heading" });

    userEvent.click(button);

    expect(screen.getByRole("heading")).toHaveTextContent(/a codesweetly project/i);
  });
});
</code></pre>
<p>上面的测试代码片段的重要内容是：</p>
<ol>
<li>引入了测试案例需要的包。</li>
<li>编写了测试案例，希望 <code>&lt;App /&gt;</code>组件可以渲染一个head元素包含 <code>"codesweetly test"</code>文本。</li>
<li>编写了另一个测试案例，模仿用户和应用按钮元素的互动。 也就是说，我们希望一旦用户点击按钮， <code>&lt;App /&gt;</code>的head就会更新<code>"a codesweetly project"</code>文本。</li>
</ol>
<p><strong>注意:</strong></p>
<ul>
<li><a href="https://jestjs.io/docs/api#describename-fn"><code>describe()</code></a>是Jest的一个全局方法。这是一个可选的方法，用户将相关的测试案例分组到一起。 <code>describe()</code>接受两个参数：
<ul>
<li>你希望测试案例组被命名的名称，如： <code>"App component"</code>.</li>
<li>包含测试案例的函数。</li>
</ul>
</li>
<li><a href="https://www.npmjs.com/package/@testing-library/user-event"><code>userEvent</code></a> 包含许多模拟用户与应用交互方法的一个React Testing Library包。例如在代码块中，我们使用 <code>userEvent</code>的<code>click()</code>方法来模拟按钮元素的点击事件。</li>
<li>每次测试案例我们都会渲染<code>&lt;App /&gt;</code>，因为每次测试后，React测试库都会卸载掉已经渲染的组件。假设你的组件有多个测试案例， 使用Jest的<a href="https://jestjs.io/docs/api#beforeeachfn-timeout"><code>beforeEach()</code></a>方法来渲染你文件中的<code>render(&lt;App /&gt;)</code>(或者<code>describe</code> 代码块中)的测试。</li>
</ul>
<h3 id="react">第十三步：重构React组件</h3>
<p>我们已经重构了测试代码，现在我们来重构<code>App</code>组件：</p>
<pre><code class="language-js">// App.js

import React, { useState } from "react";

const App = () =&gt; {
  const [heading, setHeading] = useState("CodeSweetly Test");

  const handleClick = () =&gt; {
    setHeading("A CodeSweetly Project");
  };

  return (
    &lt;&gt;
      &lt;h1&gt;{heading}&lt;/h1&gt;
      &lt;button type="button" onClick={handleClick}&gt;
        Update Heading
      &lt;/button&gt;
    &lt;/&gt;
  );
};

export default App;
</code></pre>
<p>在上述代码片段中主要发生了：</p>
<ol>
<li><code>App</code>的<code>heading</code>初始状态是<code>"CodeSweetly Test"</code>字符串。</li>
<li>编写了一个<code>handleClick</code>函数来处理<code>heading</code>状态。</li>
<li>在DOM渲染一个 <code>&lt;h1&gt;</code>和一个<code>&lt;button&gt;</code>元素。</li>
</ol>
<p>注意以下几点：</p>
<ul>
<li><code>&lt;h1&gt;</code>的内容是 <code>heading</code>状态的当前值。</li>
<li>每当用户点击按钮元素， <code>onClick()</code>事件监听器就会调用<code>handleClick()</code>函数。 <code>handleClick</code>就会更新<code>App</code>的<code>heading</code>的状态到<code>"A CodeSweetly Project"</code>。因此 <code>&lt;h1&gt;</code>的内容会改成<code>"A CodeSweetly Project"</code>。</li>
</ul>
<h3 id="">第十四步：重新执行测试</h3>
<p>一旦重构了组件之后，重新执行测试（或者检查正在运行的测试）来确保应用按照预期执行。</p>
<p>之后，在浏览器查看最近的更新。</p>
<h3 id="">就这么多！</h3>
<p>恭喜你！你成功地使用Jest和React测试库来测试React组件！ 🎉</p>
<h2 id="overview">总结</h2>
<p>本文探讨了在JavaScript和ReactJS应用中如何使用测试驱动的开发。</p>
<p>我们还学习了如何使用Jest和React测试库使得测试更加简单快速。</p>
<p>感谢阅读！</p>
<h3 id="reactjs"><strong>这里还有一些有用的ReactJS的资源：</strong></h3>
<p>我编写了一本React相关的书籍！</p>
<ul>
<li>初学者友好 ✔</li>
<li>包含代码片段 ✔</li>
<li>包含可以扩展的项目 ✔</li>
<li>和非常多好理解的例子 ✔</li>
</ul>
<p><a href="https://www.amazon.com/dp/B09KYGDQYW">React Explained Clearly</a>是你了解ReactJS的敲门砖。</p>
<p><a href="https://www.amazon.com/dp/B09KYGDQYW"><img src="https://www.freecodecamp.org/news/content/images/2022/01/Twitter-React_Explained_Clearly-CodeSweetly-Oluwatobi_Sofela.jpg" alt="React Explained Clearly Book Now Available at Amazon" width="600" height="400" loading="lazy"></a></p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ TDD 教程：如何为异步函数写测试？ ]]>
                </title>
                <description>
                    <![CDATA[ 在实际的编码过程中，会遇到很多的异步函数，比如网络请求、文件读取等。它与同步函数的不同点在于它的结果是由回调函数或 Promise 对象返回的，也就是说在调用异步函数之后，不会像同步函数那样，被调用时挂起，直到操作完成就会得到结果。 让我们开始吧 为了简单起见，这次的需求是：读取某一文件，并返回该文件的行数。 开始前，我会告诉你我们需要解决的一个问题： 由于异步函数的结果不会立刻返回，所以在测试中需要有一个东西来证明回调函数或 Promise 对象返回了结果。 假设你已经会为同步函数编写测试了【如果不会请先花几分钟看这篇 4 年后，我再次入门 TDD [http://mp.weixin.qq.com/s?__biz=MzI1NzMzMjYzMA==&mid=2247483913&idx=1&sn=26f31cb36f59d433fb62ba21e6b44ad9&chksm=ea1846f0dd6fcfe676ceacb0277d74a384b6de795349cfb350af62a9c1e75d5cd3e7403c68cb&scene=21#wechat_redirect]   ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-write-tests-for-asynchronous-functions/</link>
                <guid isPermaLink="false">5f48d7c6c8da7105cbc152af</guid>
                
                    <category>
                        <![CDATA[ 测试驱动开发 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 异步函数 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 易海门 ]]>
                </dc:creator>
                <pubDate>Fri, 28 Aug 2020 08:10:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/08/WechatIMG2750.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>在实际的编码过程中，会遇到很多的异步函数，比如网络请求、文件读取等。它与同步函数的不同点在于它的结果是由回调函数或 Promise 对象返回的，也就是说在调用异步函数之后，不会像同步函数那样，被调用时挂起，直到操作完成就会得到结果。</p><h2 id="-">让我们开始吧</h2><p>为了简单起见，这次的需求是：读取某一文件，并返回该文件的行数。</p><p>开始前，我会告诉你我们需要解决的一个问题：</p><p>由于异步函数的结果不会立刻返回，所以在测试中需要有一个东西来证明回调函数或 Promise 对象返回了结果。</p><p>假设你已经会为同步函数编写测试了【如果不会请先花几分钟看这篇 <a href="http://mp.weixin.qq.com/s?__biz=MzI1NzMzMjYzMA==&amp;mid=2247483913&amp;idx=1&amp;sn=26f31cb36f59d433fb62ba21e6b44ad9&amp;chksm=ea1846f0dd6fcfe676ceacb0277d74a384b6de795349cfb350af62a9c1e75d5cd3e7403c68cb&amp;scene=21#wechat_redirect">4 年后，我再次入门 TDD</a> 】，然后一口气就写了如下的代码：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-12.png" class="kg-image" alt="image-12" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-13.png" class="kg-image" alt="image-13" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-14.png" class="kg-image" alt="image-14" width="600" height="400" loading="lazy"></figure><p>上边的单测竟然通过了，可我设置的是 -100 行，没有比单测有误更糟的情况了。出现该问题就是因为单测不知道这个回调断言什么时候操作完成，即没有等到回调中的断言完成就结束了，所以我们来解决这个问题，加一个标识来验证回调操作完成。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-15.png" class="kg-image" alt="image-15" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-16.png" class="kg-image" alt="image-16" width="600" height="400" loading="lazy"></figure><p>通过查阅 Jest 的文档，了解到 done 就是那个处理回调的标识，但此时我们去执行，会看到一个异步回调超时的错误（默认超时是5秒）。在固定的超时时间范围内，单测还是不知道回调中的断言什么时候完成的问题，所以我们需要将这个标识放到断言语句的后边。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-17.png" class="kg-image" alt="image-17" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-18.png" class="kg-image" alt="image-18" width="600" height="400" loading="lazy"></figure><p>这个时候你会看到 Expected: -100，Received: 25，到这儿就把 -100 改为 25 就结束了，但单测还没有写完，目前仅仅是做了一个正向测试。接下来就是编写反向测试和异步测试，基本方法与 &nbsp;<a href="http://mp.weixin.qq.com/s?__biz=MzI1NzMzMjYzMA==&amp;mid=2247483913&amp;idx=1&amp;sn=26f31cb36f59d433fb62ba21e6b44ad9&amp;chksm=ea1846f0dd6fcfe676ceacb0277d74a384b6de795349cfb350af62a9c1e75d5cd3e7403c68cb&amp;scene=21#wechat_redirect">4 年后，我再次入门 TDD</a> 一样，只不过是需要为回调函数和断言语句后添加一个标识。</p><h2 id="-promise-">测试 Promise 对象</h2><p>上边描述了如何为回调函数编写测试，目的是为了简单阐述如何测试异步函数。因为大家都知道回调地狱的问题，所以出现了 Promise 这一异步解决方案，下来就介绍如何测试 Promise 对象。</p><p>有 4 种方式，第 3 和 第 4 种是常用方式，这里只介绍第 4 种书写方式，当然 4 种方式没有优劣之分，只有哪些对自己的测试更简单：</p><ul><li>使用 done 和 Promise 组合</li><li>返回 Promise</li><li>使用 async await</li><li>使用 async await 和 Promise 组合</li></ul><p>此处我的浏览器是支持 AsyncFunction 特性的，如果不支持需要选用其他方式进行测试。我们只需要在单测函数的第二个参数加上 async 关键字，并在断言语句前加上 await 关键字，还使用了 Jest 提供的 resolves 和 rejects 匹配器简化测试语句。最后，异步测试依然遵循 3As 模式。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/image-19.png" class="kg-image" alt="image-19" width="600" height="400" loading="lazy"></figure><h2 id="--1">总结</h2><p>如果学会了为同步函数写测试的方法论，对异步函数编写测试会变得很简单。但我们依然需要面临一个现实问题 ~ 依赖，它的存在会让单测很难进行自动化，故下一次将与大家探讨如何进行重构且正确地处理依赖。</p><h2 id="--2">参考</h2><ul><li>《JavaScript测试驱动开发》</li><li>Jest 官网</li></ul><p><a href="https://github.com/yihaimen/JestJsApp">代码仓库</a></p><p><a href="https://space.bilibili.com/383362014">B 站链接</a></p><p><a href="https://yihaimen.github.io/">博客地址</a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
