51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 3933|回复: 1
打印 上一主题 下一主题

[转贴] 单元测试实践,第一步就不要踩坑了!

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:32
  • 签到天数: 399 天

    连续签到: 5 天

    [LV.9]测试副司令

    跳转到指定楼层
    1#
    发表于 2020-9-24 10:28:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    你或许早已经知道“单元测试“端到端测试”这些名词,但从未真正付诸实践。在这一系列实战教程中,我们将手把手带你掌握Jest、Enzyme、Cypress等测试利器,帮助我们从bug的沼泽中挣脱出来,成为一个无往不利的高阶前端开发者!
      本篇教程是 JavaScript测试系列实战的第一篇教程,首先介绍了测试的类型,然后主要通过一个React项目教会你如何使用Jest编写第一个测试,然后使用Enzyme对React组件进行浅层渲染,以方便对不同层次的组件进行细粒度测试,当学习了这篇教程之后,你将对基础的测试编写、组件的测试有一个比较好的了解。
      初识Jest单元测试
      测试是检查代码的代码,能够大大增强我们对应用的信心。更重要的是,测试会阻止你在修复一件事情的同时破坏另一件事情,让我们能够放开手脚进行功能的添加与大规模重构。您可以测试应用程序的许多方面,从单个函数及其返回值到在浏览器中运行的复杂应用程序。万丈高楼平地起,让我们先来了解一下有哪些测试。
      测试的类型
      单元测试
      单元测试的目标可以是一个函数,一个类,或者一个模块。单元测试应该是相互隔离和独立的。对于给定的输入,单元测试检查结果。通过及早发现问题并避免bug回归,它可以帮助我们确保代码的各个部分按预期工作。
      集成测试
      即使所有单元测试都通过了,我们的应用仍然可能会崩溃。集成测试则是用来测试跨模单元/模块的过程,可以很好地确保我们的代码能够作为一个整体运行。
      端到端测试(E2E)
      与其他类型的测试不同,E2E测试总是在浏览器(或类浏览器)环境中运行。它可能是一个实际的浏览器,可以打开并在其中运行测试;也可能是一个无头(Headless)的浏览器环境,这是一个没有用户界面的浏览器。E2E测试的重点是在我们正在运行的应用程序中模拟实际用户(例如模拟滚动、单击和键入等行为),并检查我们的应用程序是否从实际用户的角度运行良好。
      在这一系列教程中,我们将会从零开始,一步步带你熟悉从单元测试到端到端测试的方方面面。我们将会在一个React项目中实践所学到的[url=]自动化测试[/url][url=]技术[/url]。首先用CreateReactApp(CRA)搭建项目脚手架:
    1. create-react-app javascript-test-series
    复制代码
    然后我们删除 src 目录下所有预创建的文件(当然你也可以手动删除):
    1. rm src/*
    复制代码
    一切准备就绪!让我们开始吧。
      编写第一个单元测试
      编写一个单元测试实际上要比你想象得简单很多。首先创建divide.js,在其中编写一个divide函数:


    1. // divide.js

    2. function divide(a, b) {

    3.   return a / b;

    4. }

    5. module.exports = divide;
    复制代码
    然后创建测试文件divide.test.js,代码如下:

    1. // divide.test.js

    2. const divide = require('./divide');




    3. test('dividing 6 by 3 equals 2', () => {

    4.   expect(divide(6, 3)).toBe(2);

    5. });
    复制代码
    作为本系列教程的第一个Jest测试,我们来详细讲解一下:
      我们先导入需要测试的单元/模块
      test函数定义了一个[url=]测试用例[/url],第一个参数就是用例描述,一般是一句完整的描述,例如上面的dividing6by3equals2;第二个参数则是一个待执行的测试函数。
      在测试函数中,最重要的组成部分就是断言(Assertion),例如上面的expect(divide(6,3)).toBe(2)。
      断言的核心是 expect函数,它接受一个表达式,然后后面可以调用Matcher来测试该表达式是否符合条件,例如这里我们就使用了最常用的toBeMatcher;Jest还提供了大量的Matcher,可以帮助我们写出更简洁可读的断言语句。
      CRA已经为我们配置好了Jest,这里直接运行npxjest命令,就可以看到测试结果了:


    1. PASS  ./divide.test.js

    2.   ✓ dividing 6 by 3 equals 2 (5ms)
    复制代码
    提示
      CRA也配置了test命令,但是提供了比较复杂的功能配置(例如Watch模式等),可能会让初学Jest的你不知所措。因此这里建议直接使用npxjest执行测试。
      编写第一组测试
      每个测试文件通常有多个测试用例。Jest允许我们通过describe函数对测试用例进行分组,它创建了一个可以组合多个测试的块。让我们对全局Math对象运行一些测试(希望浏览器工程师和Node开源项目维护者不要来打我),创建math.test.js,代码如下:


    1. // math.test.js

    2. describe('in the math global object', () => {

    3.   describe('the random function', () => {

    4.     it('should return a number', () => {

    5.       expect(typeof Math.random()).toEqual('number');

    6.     });




    7.     it('should return a number between 0 and 1', () => {

    8.       const randomNumber = Math.random();

    9.       expect(randomNumber).toBeGreaterThanOrEqual(0);

    10.       expect(randomNumber).toBeLessThan(1);

    11.     });

    12.   });




    13.   describe('the round function', () => {

    14.     it('should return a rounded value of 4.5 being 5', () => {

    15.       expect(Math.round(4.5)).toBe(5);

    16.     });

    17.   });

    18. });
    复制代码
    你也许注意到了这里我们用了it函数而不是test函数,这两者实际上是完全一样的。
      这样对测试进行分组可以使我们的代码更加清晰。在关注应用程序的代码质量的同时,我们也应该确保测试代码的质量,这样我们才有足够的动力不断去维护测试代码,从而确保我们的项目能够保持健壮。
      除了使代码更具可读性之外,它还有助于在出现错误时提供更好的错误消息。如果这里我们将第一条测试用例改为expect(typeofMath.random()).toEqual('string'),那么再运行npxjest,就会出现如下错误信息:


    1. FAIL  ./math.test.js

    2.   ● in the math global object › the random function › should return a number




    3.     expect(received).toEqual(expected)




    4.     Expected value to equal:

    5.       "string"

    6.     Received:

    7.       "number"
    复制代码
    是不是一目了然呢?
      小结
      在这一小节中,我们首先了解了测试有哪些类型。然后我们在CRA脚手架中编写了一个简单的函数,并为之编写了第一个单元测试,熟悉了测试用例、断言、Matcher这些关键概念,并成功地通过了测试。接着,我们又编写了一个包含多个用例的测试文件,并通过describe函数将测试用例组织得井井有条。
      初识Enzyme:编写第一个React组件测试
      很显然,我们不会仅仅满足于测试像divide那样简单的函数,我们希望能够测试一个React组件,但是和一个普通的JavaScript函数不同,测试一个React组件还需要两个关键的问题:1)怎么渲染待测试的组件;2)怎么测试渲染出来的组件。
      所幸的是,Airbnb作为重度使用React的先驱,早就提出了专门的解决方案:Enzyme。
      安装和配置Enzyme
      首先安装Enzyme和相应的React适配器:

    1. npm install enzyme enzyme-adapter-react-16
    复制代码
     我们需要配置一下Enzyme,才能在Jest测试文件中使用它。创建src/setupTests.js,代码如下:

    1. // src/setupTests.js

    2. import { configure } from 'enzyme';

    3. import Adapter from 'enzyme-adapter-react-16';


    4. configure({ adapter: new Adapter() });
    复制代码
    浅层渲染
      Enzyme提供的一个重要功能便是组件的浅层渲染(ShallowRendering)。它允许我们在运行测试时,只渲染父组件而不渲染其所有的子组件。浅层渲染十分快速,因此非常适合单元测试。
      首先让我们创建一个简单的React组件,创建src/App.js,代码如下:

    1. // src/App.js

    2. import React from 'react';




    3. const App = () => {

    4.   return <h1>Hello world!</h1>;

    5. };




    6. export default App;
    复制代码
    编写App组件对应的测试文件src/App.test.js,代码如下:

    1. // src/App.test.js

    2. import React from 'react';

    3. import { shallow } from 'enzyme';




    4. import App from './App';




    5. describe('app component', () => {

    6.   it('contains a header with the "Hello world!"', () => {

    7.     const app = shallow(<App />);

    8.     expect(app.containsMatchingElement(<h1>Hello world!</h1>)).toEqual(true);

    9.   });

    10. });
    复制代码
    可以看到,这里我们用shallow函数来浅层渲染App组件得到app,并且调用其containsMatchingElement来判断渲染后的App组件是否包含<h1>Helloworld!</h1>元素。
      通过npmtest命令,我们就可以看到刚才的测试通过了:


    1. PASS  app/App.test.js

    2.   app component

    3.     ✓ contains a header with the "Hello world!"
    复制代码

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    昨天 09:32
  • 签到天数: 399 天

    连续签到: 5 天

    [LV.9]测试副司令

    2#
     楼主| 发表于 2020-9-24 10:28:28 | 只看该作者
    测试更复杂的组件
      在实际的前端开发中,我们的组件要复杂很多。本着循序渐进的原则,我们稍微前进一步:来编写一个接受props的组件,并根据数据来决定渲染结果。
      配置jest-enzyme
      你应该还记得,在刚才的测试代码中,我们还是使用了Jest自带的Matcher(toEqual)。但实际上,社区还提供了更好的选择——专门为Enzyme定制的Matcher库:enzyme-matchers。这些Matcher使得编写断言语句更轻松、更具可读性。
      我们通过npm来安装jest-enzyme:

    1. npm install jest-enzyme
    复制代码
    相应地在src/setupTests.js中添加相应的配置:

    1. // src/setupTests.js

    2. import { configure } from 'enzyme';

    3. import Adapter from 'enzyme-adapter-react-16';

    4. import 'jest-enzyme';




    5. configure({ adapter: new Adapter() });
    复制代码
     编写TodoList组件
      这次,我们还是编写一个熟悉的TodoList组件。创建src/TodoList.js,代码如下:


    1. // src/TodoList.js

    2. import React from 'react';




    3. const ToDoList = (props) => {

    4.   return (

    5.     <ul>

    6.       {props.tasks.map((taskName, index) => (

    7.         <li key={index}>{taskName}</li>

    8.       ))}

    9.     </ul>

    10.   );

    11. };




    12. export default ToDoList;
    复制代码
    可以看到,这个组件接受一个tasks数组,并将其渲染成一个列表。
      编写TodoList组件测试
      先思考一下,如果要测试上面的TodoList组件,要考虑哪些情况?不难想到主要是两种情况:
      传入的tasks数组为空
      传入的tasks数组不为空
      对应这两种情况,我们开始编写测试。创建src/TodoList.test.js,代码如下:


    1. // src/TodoList.test.js

    2. import React from 'react';

    3. import { shallow } from 'enzyme';




    4. import ToDoList from './ToDoList';




    5. describe('ToDoList component', () => {

    6.   describe('when provided with an empty array of tasks', () => {

    7.     it('contains an empty <ul> element', () => {

    8.       const toDoList = shallow(<ToDoList tasks={[]} />);

    9.       expect(toDoList).toContainReact(<ul />);

    10.     });




    11.     it('does not contain any <li> elements', () => {

    12.       const toDoList = shallow(<ToDoList tasks={[]} />);

    13.       expect(toDoList.find('li').length).toEqual(0);

    14.     });

    15.   });




    16.   describe('when provided with an array of tasks', () => {

    17.     it('contains a matching number of <li> elements', () => {

    18.       const tasks = ['Wash the dishes', 'Make the bed'];

    19.       const toDoList = shallow(<ToDoList tasks={tasks} />);

    20.       expect(toDoList.find('li').length).toEqual(tasks.length);

    21.     });

    22.   });

    23. });
    复制代码




    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-4-20 08:58 , Processed in 0.065766 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表