技术控

    今日:59| 主题:49369
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 如何使用 Jest 测试 React 组件

[复制链接]
撒浪嘿哟 发表于 2016-10-18 14:12:27
88 5

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
在这篇文章中我们将学习一下如何使用Jest来测试我们的ReactJS组件,Jest是一个由Facebook维护的测试框架。在学习它提供的使测试React apps更容易的      新特性之前,我们先了解一下如何用Jest测试简单的JavaScript函数,Jest没有特别针对React,你可以用它测试任何JavaScript程序。然而,它提供了一些特性便于测试用户接口,这也是为什么它非常适合React。   
        示例应用程序   

    在测试之前,我们先得有一个应用程序来做测试才行! 秉承WEB开发的优良传统,我整了一个待办事项应用程序来做起步阶段的练手项目。在      GitHub上你可以找到这个项目,我把它跟所有的测试代码放到了一起。如果想要先用用这个应用程序,好让你自己能具体了解到它可以做些什么,那么,你可以点击      这里看到一个在线运行的示例版本。   
    程序是用 ES2015 写的, 编译时要用到预置了 ES2015 和 React 的 Webpack。关于构建的细节我不会讲太多,如果你想了解更多的话,这些资源都在      GitHub上。关于如何在本地让项目运行起来,在 README 中有完整的说明。程序是使用       Webpack构建的,所以如果你想要了解更多的东西的话, 我的建议是读一读      《Webpack 入门指南》,它对这个工具有很好的介绍。   
    app/index.js 是这个应用程序的入口, 它只是将 Todos 组件渲染到了 HTML 里面:
   
  1. render(
  2.   <Todos />,
  3.   document.getElementById('app')
  4. );
复制代码
   Todos 组件是这个程序的重中之重,它包含了所有的状态(在这个程序里面是硬编码的——实际应用中要从一个API或者是其它类似的来源那里获取才好),还有一些代码是用来渲染两个子组件的: Todo, 在状态里面的每一个待办事项都会让它被渲染一遍,还有就是 AddTodo, 它会被渲染一次,提供给用户一个表单来添加新的待办事项。
    因为 Todos 组件包含了所有的状态, 所以就需要 Todo 和 AddTodo 组件来通知它啥时候有改变发生。由此它会在某些数据发生改变时给这些组件下发一些函数来调用,而 Todos 也就能够有依据地对状态进行更新。
        最后,也就是现在,你会发现,所有的业务逻辑都包含在了app/state-functions.js中。
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}
复制代码
     这些都是纯函数,它们可以接受状态和一些数据,并返回新的状态。如果你对纯函数不太熟悉的话,他们只是提供一些参考数据的函数,并没有其它什么副作用。要了解更多相关内容,你可以阅读        我在A List Apart上面写的有关纯函数的文章以及        SitePoint上有关纯函数和React的文章。      
      如果你熟悉Redux,他们和Redux中称为减速器的东西非常类似。事实上,如果应用程序的规模变得很大,我会考虑转而使用Redux,因为这样可以得到一个更明确的、结构化的数据。但对于这种规模的应用程序,你通常会发现,本地组件状态并具有良好的抽象功能,这才是最重要的。
            用还是不用 TDD 方法呢?   

    许多的文章都描述过有关于测试驱动开发(Test Driven Development      )的利弊,它所倡导的就是开发者实现写好测试代码,然后再编写代码来满足这些测试的要求。这个创意的背后意义就是通过先把测试写好,让你不得不去深入思考你所要编写的API,如此就可以得到一个更好的设计。在我看来,这很多都是要因人而定的,而且也要看测试的是什么东西。我发现对于 React组件来说,自己更喜欢先把组件写好,然后再对功能最重要的部分添加一些测试。不过不过你发现先给组件写好测试代码适合于自己的工作流程,那么就应该这样做,其实没啥硬性规定的。只要对你和你的团队有利,怎样都好。   
    Jest 介绍

          Jest第一次发布是在 2014 年,虽然它在最初获得了很多关注,但后面沉寂了一段时间没有进行积极的研发。不过在去年由 Facebook 发起的投资,对 Jest 进行改善,并且在最近发布了一个新的版本,带来了一些令人印象深刻的改变,重新引发了我们的关注。同最开始的开放源代码版本作比较,Jest与其的相似之处只有名称和logo,而其它所有的东西都已经改变和重写了。如果你想了解更多,可以读一读       Christoph Pojer 的评论,里面有他对项目当前状态的讨论。   
    如果你在使用其它框架对  Babel, React 和 JSX 进行测试时遇到过挫折,那么我绝对会推荐你尝试一下 Jest。如果你发现现有的测试设置太慢了,我也会强烈推荐Jest。它可以自动地让测试并行运行,这个在当你有一个很大的测试项目要跑的时候会很有价值。它附带有对      JSDom的配置, 意味着你可以在浏览器里面编写测试,然后通过 Node 将它们跑起来,如此即可应对异步功能的测试,并且它还拥有一些高级的功能,比如内置的MOCK测试,SPY测试以及STUB测试。   
        安装和配置Jest   

    首先我们需要安装Jest。因为要同时使用Babel,所以我们还需安装另外几个模块,以使得Jest和Babel可以协同工作:
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest
复制代码
   你还需要为Babel配置一个.babelrc文件,这样可以使用所需的任何预设及插件。示例项目已经包含了该文件,就像:
   
  1. {
  2.   "presets": ["es2015", "react"]
  3. }
复制代码
   我们还没有安装任何的React测试工具,因为我们还不打算开始测试我们的部件,但是我们的状态函数除外。
    Jest希望在__tests__文件夹中找到我们的测试,这已成为JavaScript社区的惯例,我们也打算坚持这样做。如果你不喜欢__tests__这个设置,Jest也支持寻找任意的.test.js和.spec.js文件。
    因为我们要测试我们的状态函数,所以继续下一步,创建__tests__/state-functions.test.js。
    然后我们将编写一个测试文件,不过现在要进行dummy测试,这样可以检查所有工作是否正常以及对Jest的配置情况。
   
  1. describe('Addition', () => {
  2.   it('knows that 2 and 2 make 4', () => {
  3.     expect(2 + 2).toBe(4);
  4.   });
  5. });
复制代码
   现在,进入你的package.json。我们需要设置npm test,以便它运行Jest,只需通过设置test脚本来运行Jest就可以。
   
  1. "scripts": {
  2.   "test": "jest"
  3. }
复制代码
   如果你现在在本地运行npm test,你可以看到测试运行      正常,并通过。   
   
  1. PASS  __tests__/state-functions.test.js
  2.   Addition
  3.     ✓ knows that 2 and 2 make 4 (5ms)
  4. Test Suites: 1 passed, 1 total
  5. Tests:       1 passed, 1 total
  6. Snapshots:   0 passed, 0 total
  7. Time:        3.11s
复制代码
     如果你曾经使用过Jasmine,或者大多数测试框架,那么对于以上的测试代码你应该相当熟悉。Jest让我们根据需要进行usedescribeanditto nest测试。至于要使用多少个nest完全取决于你了;我喜欢所有的描述性字符可以通过,todescribeanditread几乎是一个完整的句子。
      当要进行实际的声明时,你会希望在此之前通过expect()调用进行测试。在这种情况下,我们使用toBe。你可以        在Jest文档找到所有可用的声明列表,toBe会使用===检查给定的值是否匹配测试中的值。在本教程中,我们会遇到几个Jest的声明。      
            测试业务逻辑   

    当看到Jest可以在模拟测试上运行时,      接着就可以在真实案例上运行了!接下来测试的是第一个状态方法,toggleDone。toggleDone的参数是即将切换状态的todo的当前状态和它的ID。每个todo都有一个done属性,toggleDone方法应该将它由true改为false,或者相反的变更。   
    如果你在按本文步骤操作的话,请确保你已经克隆这个            仓库,并已拷贝到和你的___tests__文件夹同一个父文件夹下。同时也需要安装shortid包,(npm install shortid --save) 这个Todo app所依赖的包。   
    先从      app      /      state      -      functions      .      js中引入待测试方法,然后建立测试结构。虽然Jest允许无限级的嵌套调用dsecribe和it,你还可以用test方法,因为它往往看起来更容易阅读。Jest中的test方法只是it的一个别名,但是有时可以让测试代码更易于阅读也不用很多的嵌套。   
    使用嵌套的describe和it方法,我可能写出如下案例:
   
  1. import { toggleDone } from '../app/state-functions';
  2. describe('toggleDone', () => {
  3.   describe('when given an incomplete todo', () => {
  4.     it('marks the todo as completed', () => {
  5.     });
  6.   });
  7. });
复制代码
   而用test则可能是下面这个样子:
   
  1. import { toggleDone } from '../app/state-functions';
  2. test('toggleDone completes an incomplete todo', () => {
  3. });
复制代码
   test方法的看起来更好,但是缩进可能会少一点。这主要与个人喜好相关,你可以选一个你更习惯的书写方式。
    接着就可以写断言了。首先,在传入toggleDone之前先创建一个初始状态,同时传入需要转换状态的todo的ID。基于toggleDone方法返回的完成状态,我们这样写断言:
   
  1. const startState = {
  2.   todos: [{ id: 1, done: false, name: 'Buy Milk' }]
  3. };
  4. const finState = toggleDone(startState, 1);
  5. expect(finState.todos).toEqual([
  6.   { id: 1, done: true, name: 'Buy Milk' }
  7. ]);
复制代码
   注意这里是用toEqual作的断言。对于原始类型,如字符串和数字,应该使用toBe方法。toEqual是用于处理数字和对象的,它会递归的比较给定对象的所有字段或者元素以确认相等。
    至此,我们便可以运行npm test,然后可以看到这个状态方法通过了测试:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}0
复制代码
       变更后重新运行测试   

    更改测试文件,需要再次手动运行 npm test,这点令人沮丧。Jest 的最佳功能是在观察模式,它会观察文件的更改来运行测试。它依据哪些文件被更改,找出哪些子集需要测试运行。这令人难以置信的强大和可靠,用户可以把 Jest 运行在观察模式,然后将其放在一边,之后的一整天都可以只专注于修改代码了。
    在观察模式下运行,可以运行 npm test -- --watch。在 npm test 之后,第一个 -- 之前,将命令直接传递给底层。以下这两个命令是等效的:
   
          
  •         npm test -- --watch
          
  •         jest --watch
       
    我建议把 Jest 的运行留在另一个 tab 或者终端窗口中。
    在测试 React 组件之前,我们对其中一个状态函数编写一个测试。在真实的应用程序中可能需要编写很多测试,但是为了本教程,我跳过了他们。现在,让我们编写一个测试,确保我们的 deleteTodo 函数工作。你可以尝试自己编写并与你自己的测试进行比较。
                  显示测试         
    测试的第一部分改变不多,我们设置了初始状态运行我们的函数并完成维护状态。如果你离开 Jest 运行着的观察者模式,那么你的新测试将不能被检测到,也不会被运行,更不能像在 Jest 中那么快!在你测试,编写它们的时候,这种获得即时反馈的方式是很伟大的。
    上面的测试证明了对一个测试的完美安排是这样的:
   
          
  •         建立
          
  •         在测试之下执行函数
          
  •         反馈结果(结果断言)
       
    以这种方式通过测试,你会发现他们更容易被跟踪和处理。
    现在我很乐于测试我们的状态函数,让我们把目光转移到 React 组件吧。
        测试React组件

    出于其意义甚微,事实上我一般不推荐为React组件写太多的测试。你需要测试的东西,比如业务逻辑,本来就应该从组件中抽离出去,然后落实到独立的方法中去,比如我们上文测试的状态方法。也即是说,测试React的交互(比如,确保当用户点击一个按钮是指定的方法会以正确的参数被调用)只是偶尔才有用。我们将从测试React组件可以渲染正确的数据开始测试,然后测试交互。然后进行的是快照,Jest的一个可以使测试React组件输出变得方便的特性。
    因此我们必须使用      react      -      addons      -      test      -      utils,一个                  提供了          测试React方法的库        。还需要安装        Enzyme,        AirBnB写的一个可以使测试React组件变得简单的包装库。我们将        在所有的测试中使用这个API。        Enzyme是一个十分杰出的库,甚至React团队都在推荐用它测试React组件。         
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}1
复制代码
   我们将测试Todo组件能否将它的todo文本渲染到一个段落。首先要创建      __tests__      /      todo      .      test      .      js,然后导入我们的组件:   
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}2
复制代码
   这里也从Enzyme中导入了mount。mount方法是用于渲染组件,      其输出则用于检查和断言。虽然是在Node中测试,我们却可以在写测试时引入DOM元素。因为Jest配置了      jsdom      ,一个在Node中实现了DOM的库。这是一个很棒的库,通过它我们就可以写基于DOM的测试,且        在测试的时候不用每次都打开浏览器。         
        可以用mount方法创建Todo:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}3
复制代码
   然后调用wrapper.find方法,传入一个CSS选择器,用于查找需要包含Todo内容的段落。这个API可能会让你想起jQuery,因为它是故意这么设计的。在渲染的输出中查找匹配的元素用时使用这个API看起来十分直观:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}4
复制代码
   最后,断言它包含的文本是Buy Milk:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}5
复制代码
   完整的测试代码将会是下面这个样子:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}6
复制代码
   哇哦!可能你觉得为了测试‘Buy Milk’可以显示到正确的位置的确需要很多的工作,呃……确实如此。但是先不必着急,下一节将会看到用Jest的快照功能会使这个测试变简单很多。
    此时请先看一下,如何用Jest的监听功能来断言方法是否以指定参数调用。对于本例来说这个测试是很有用的,因为我们的Todo组件包含了两个函数类型的属性,它们是在用户点击按钮或者触发交互时被调用的。
    下面的测试会断言当点击todo按钮的时候,会调用给定组件的doneChange方法。
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}7
复制代码
   现在需要有个可以跟踪方法调用以及调用时的参数的函数。然后就可以检查当用户点击todo时,doneChange会被以正确的参数调用。幸好,Jest提供了开箱即用的监听功能。监听方法是无需关注其实现,只需考虑其调用时机及方式的方法。可以想象为你在监视那个方法。可以调用Jest.fn()创建:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}8
复制代码
   返回的方法可以用于监听以检查其是否被正确调用。一开始要渲染一个有正确属性的Todo:
   
  1. export function toggleDone(state, id) {...}
  2. export function addTodo(state, todo) {...}
  3. export function deleteTodo(state, id) {...}9
复制代码
   接着,再次查找那个段落,如同上一个测试:
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest0
复制代码
   然后可以调用它的simulate方法,模拟用户事件,传入参数click:
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest1
复制代码
   最后只需断言监视方法可以被正确调用。本例中,期望调用时传入todo的ID也就是1。可以用expect(doneChange).toBeCalledWith(1)来断言,此时也完成了我们的测试!
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest2
复制代码
       使用组件的快速测试更好   

    我上面提及的看起来测试React组件需要大量工作,特别是一些比较通用功能(比如渲染文本)。Jest允许你快速测试,而不是在React组件里使用大量断言。这些对于交互没有多大用处(但是我依然选择使用我上面提及的),但是对于测试组件的输出是否正确,它使用起来非常简单。
    当你运行一个快速测试,Jest在测试时渲染React组件,并且益JSON格式文件来存储结果。每次运行测试,Jest将会快速检测React组件是否依然渲染相同输出。然后,当你改变一个组件的行为,Jest将会告诉你下面的其中一种情况:
   
          
  •         你意识到了错误,同时你可以修复组件使之再次匹配快速测试。
          
  •         或者,你特意做出修改,然后通过Jest来更新快速测试。
       
    这种测试方式意味着:
   
          
  •         你不需要写大量断言来确认你的React组件与你期待是否相符。
          
  •         你可以任意时刻改变组件方法,因为Jest会识别。
       
    你也不用对你所有组件进行快速测试,事实上,我不推荐这种方式。你可以选择一些需要确认的组件进行功能测试。快速测试你所有组件将会使得你的测试周期变长,效率更低。请谨记,React是一个非常好用的测试框架,所以我们大可以对它的测试结果放心。请确认你没有结束测试框架,而不是你的代码。
        进行快照测试之前,还需要另一个Node包。      react-test-renderer      是一个将React组件渲染成一个纯JavaScript对象的包。也意味着其结果可以被存储成文件,这也正是Jest用来跟踪快照的方法。   
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest3
复制代码
   下面开始用快照方式重写第一个Todo组件测试。先将TodoComponent中点击todo调用doneChange的测试注释掉。
    首先要做的是引入react-test-render,并删除mount的引用。它们不能同时使用,只能使用其中之一。所以我们要先注释掉那个测试。
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest4
复制代码
   然后用刚才导入的renderer渲染组件,并断言它与快照相符:
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest5
复制代码
   第一次运行这段代码,Jest可以发现没有这个组件的快照,然后会创建一个。看一下      __tests__      /      __snapshots__      /      todo      .      test      .      js      .      snap:   
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest6
复制代码
   可以看到,Jest将输出保存了起来,下一次运行这个测试的时候,它会检查输出是否与快照相同。为了举例,先将组件破坏,将渲染了todo文本的段落移除,也就是将这段代码从TodoComponent中删除:
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest7
复制代码
   现在看看Jest的输出:
   
  1. npm install --save-dev babel-jest babel-polyfill babel-preset-es2015 babel-preset-react jest8
复制代码
   Jest发现快照与新组件并不匹配,在结果中就体现出来了。如果这次变更是正常的,可以在运行jest时加上-u参数,这将会更新快照。但是本例中,先重置变更,Jest会再次变得高兴。
    下一步,想想该如何使用快照测试交互。同一个测试可以拥有多个快照,所以可以测试交互后的输出与预期是否相符。
    其实,并不能用Jest快照测试我们Todo组件的交互,因为他们不控制本身的状态,而是调用其callback属性上的回调函数。本例中已经将快照测试移到一个新文件      todo.snapshot.test.js,将原先的toggling测试保留在             todo.test.js      。将快照测试单独放到一个文件是有用的,也意味着消除了        react        -        test        -        renderer 和 react        -        addons        -        test        -        utils之间的冲突。         
    注意,在      GitHub       上可以找到这个教程中全部的代码,也可以切到你本地运行。   
        结论

    Facebook发布Jest已经有很长一段时间,但在最近几个月它才被挖出来并充分被使用。它很快得到JavaScript开发人员的青睐,并将会变得更好。如果过去试用过Jest,但对它并不满意,我建议你再试一次,因为它现已做出非常大的改变:运行快,操作规范,错误消息提示奇特,更妙的是它还具备快照功能。
友荐云推荐




上一篇:Doudou Linux – Every homeschooler&#x27;s must have operating system
下一篇:如何在使用 Flux 时使 ReactJS 更好地发挥作用
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

lalisaa88 发表于 2016-10-18 20:55:10
楼主已成仙,有事请求签!
回复 支持 反对

使用道具 举报

usifb 发表于 2016-10-22 00:49:43
经验告诉哥,不能弯腰捡肥皂啊!
回复 支持 反对

使用道具 举报

马云 发表于 2016-11-5 14:10:04
这么好的贴,家里人知道吗
回复 支持 反对

使用道具 举报

电商令狐冲 发表于 2016-11-8 22:38:05
阅!谢谢分享
回复 支持 反对

使用道具 举报

vip514 发表于 2016-11-12 03:59:53
听说摇动显示器可加撒浪嘿哟好友!
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表