Skip to content

Latest commit

 

History

History
369 lines (268 loc) · 14.6 KB

File metadata and controls

369 lines (268 loc) · 14.6 KB

九、单元和行为测试的持续集成

我们成功了;祝贺我们已经创建了一个在某个域名下运行的全栈应用(在本书中是reactjs.space)。整个设置中缺少的部分是部署过程。部署应该是零停机时间。我们需要有一个应用的冗余服务器。

我们的应用中还缺少一些步骤,使其能够作为小型化、单元和行为测试进行专业工作。

在本章中,我们将向您介绍掌握全栈开发所需的一些附加概念。剩下的部分留给你做家庭作业。

何时编写单元测试和行为测试

通常,关于何时编写单元测试和/或行为测试,有一些建议。

我们在波兰经常有客户经营初创企业。作为对他们的总体治理,我们建议如下:

  • 如果你的创业公司正在寻找牵引力,你需要你的产品来实现它,那么不要担心测试
  • 在您创建了您的最低可行产品MVP之后,在扩展应用时必须进行这些测试
  • 如果你是一家成熟的公司,正在为你的客户开发应用,并且你非常了解他们的需求,那么你必须进行测试

前两点与初创公司和年轻公司有关。第三点主要与老牌公司有关。

根据您和您的产品所处的位置,您需要自行决定是否值得编写测试。

反应惯例

中,有一个项目充分展示了全栈开发设置的外观 http://ReactJS.co

访问此网站,了解如何将应用与单元测试和行为测试集成,并了解有关如何制作 React Redux 应用的最新最佳约定。

测试业力

我们不会指导您设置本章中的测试,因为它不在本书的范围内。本章的目的是为您提供在线资源,帮助您了解全局

Karma 是单元测试和行为测试最流行的工具之一。主要目标是在处理任何应用时提供一个高效的测试环境。

使用此测试运行程序可以提供许多功能。有一段很好的视频解释了关于因果报应的大局 https://karma-runner.github.io

其中一些主要特点如下:

  • 在真实设备上的测试:您可以使用真实浏览器和真实设备(如手机、平板电脑或 PhantomJS)来运行测试(PhantomJS 是一个无头 WebKit,可使用 JavaScript API 编写脚本;它对各种 web 标准具有快速和本机支持:DOM 处理、CSS 选择器、JSON、画布和 SVG),但有一种工具可以在所有这些工具上运行。
  • 远程控制:您可以远程运行测试,例如,在 IDE 的每次保存上,这样您就不必手动执行测试。
  • 测试框架不可知:您可以用 Jasmine、Mocha、QUnit 和其他框架编写测试。这完全取决于你。
  • 持续整合:Karma 与诸如 Jenkins、Travis 或 CircleCI 等 CI 工具配合使用效果非常好。

如何编写单元测试和行为测试

让我们提供一个示例,说明如何正确设置项目,以便能够编写测试。

访问非常流行的 Redux 入门套件的 GitHub repo,网址为https://github.com/davezuko/react-redux-starter-kit

然后访问该存储库的package.json文件。我们可以在那里找到可能的命令/脚本:

 "scripts": { 
    "clean": "rimraf dist", 
    "compile": "better-npm-run compile", 
    "lint": "eslint src tests server", 
    "lint:fix": "npm run lint -- --fix", 
    "start": "better-npm-run start", 
    "dev": "better-npm-run dev", 
    "dev:no-debug": "npm run dev -- --no_debug", 
    "test": "better-npm-run test", 
    "test:dev": "npm run test -- --watch", 
    "deploy": "better-npm-run deploy", 
    "deploy:dev": "better-npm-run deploy:dev", 
    "deploy:prod": "better-npm-run deploy:prod", 
    "codecov": "cat coverage/*/lcov.info | codecov" 
  },

您可以发现,在 NPM 测试之后,它运行以下命令:

   "test": { 
      "command": "babel-node ./node_modules/karma/bin/ 
       karma start build/karma.conf", 
      "env": { 
        "NODE_ENV": "test", 
        "DEBUG": "app:*" 
      } 
    }

您可以从中找到位于build/karma.conf的 Karma 配置文件 https://github.com/davezuko/react-redux-starter-kit/blob/master/build/karma.conf.js

内容(2016 年 7 月)如下:

import { argv } from 'yargs' 
import config from '../config' 
import webpackConfig from './webpack.config' 
import _debug from 'debug' 

const debug = _debug('app:karma') 
debug('Create configuration.') 

const karmaConfig = { 
  basePath: '../', // project root in relation to bin/karma.js 
  files: [ 
    { 
      pattern: `./${config.dir_test}/test-bundler.js`, 
      watched: false, 
      served: true, 
      included: true 
    } 
  ], 
  singleRun: !argv.watch, 
  frameworks: ['mocha'], 
  reporters: ['mocha'], 
  preprocessors: { 
    [`${config.dir_test}/test-bundler.js`]: ['webpack'] 
  }, 
  browsers: ['PhantomJS'], 
  webpack: { 
    devtool: 'cheap-module-source-map', 
    resolve: { 
      ...webpackConfig.resolve, 
      alias: { 
        ...webpackConfig.resolve.alias, 
        sinon: 'sinon/pkg/sinon.js' 
      } 
    }, 
    plugins: webpackConfig.plugins, 
    module: { 
      noParse: [ 
        //sinon.js/ 
      ], 
      loaders: webpackConfig.module.loaders.concat([ 
        { 
          test: /sinon(|/)pkg(|/)sinon.js/, 
          loader: 'imports?define=>false,require=>false' 
        } 
      ]) 
    }, 
    // Enzyme fix, see: 
    // https://github.com/airbnb/enzyme/issues/47 
    externals: { 
      ...webpackConfig.externals, 
      'react/addons': true, 
      'react/lib/ExecutionEnvironment': true, 
      'react/lib/ReactContext': 'window' 
    }, 
    sassLoader: webpackConfig.sassLoader 
  }, 
  webpackMiddleware: { 
    noInfo: true 
  }, 
  coverageReporter: { 
    reporters: config.coverage_reporters 
  } 
} 

if (config.globals.__COVERAGE__) { 
  karmaConfig.reporters.push('coverage') 
  karmaConfig.webpack.module.preLoaders = [{ 
    test: /.(js|jsx)$/, 
    include: new RegExp(config.dir_client), 
    loader: 'isparta', 
    exclude: /node_modules/ 
  }] 
} 

// cannot use `export default` because of Karma. 
module.exports = (cfg) => cfg.set(karmaConfig)

正如你在karma.conf.js中所看到的,他们正在使用摩卡咖啡(用"frameworks: ['mocha']"检查线路)。配置文件中使用的其他选项在提供的文档中进行了说明 http://karma-runner.github.io/1.0/config/configuration-file.html 。如果你有兴趣学习业力配置,那么karma.conf.js应该是你的起始文件

什么是摩卡咖啡?你为什么需要它?

在 Karma 配置文件中,我们发现它使用 Mocha 作为 JS 测试框架(https://mochajs.org/ )。让我们分析一下代码库。

我们可以在config/index.js文件中找到dir_test : 'tests',因此基于该变量,Karma 的config知道摩卡的测试位于tests/test-bundler.js文件中。

让我们看看tests目录中的内容 https://github.com/davezuko/react-redux-starter-kit/tree/master/tests. 正如您在test-bundler.js文件中看到的,有很多依赖项:

// --------------------------------------- 
// Test Environment Setup 
// --------------------------------------- 
import 'babel-polyfill' 
import sinon from 'sinon' 
import chai from 'chai' 
import sinonChai from 'sinon-chai' 
import chaiAsPromised from 'chai-as-promised' 
import chaiEnzyme from 'chai-enzyme' 

chai.use(sinonChai) 
chai.use(chaiAsPromised) 
chai.use(chaiEnzyme()) 

global.chai = chai 
global.sinon = sinon 
global.expect = chai.expect 
global.should = chai.should()

让我们大致描述一下这里使用了什么:

  • Babel polyfill 模拟完整的 ES6 环境
  • Sinon 是一个独立的、与测试框架无关的 JavaScript 测试,用于间谍、存根和模拟

如果在一段经过测试的代码中,您需要其他外部用户的服务,那么间谍是有用的。您可以检查它是否被调用,它有哪些参数,它是否返回了什么,甚至它被调用了多少次!

存根概念与间谍概念非常相似。最大的区别是存根替换了目标函数。它们还将调用的代码替换为自定义行为(替换它),例如抛出异常或返回值。它们还能够调用作为参数提供的回调函数。存根代码返回指定的结果。

模拟是一种更聪明的存根。mock 用于断言数据,当存根仅用于返回数据且不应断言数据时,mock 不应返回数据。mock 可以归档您的测试(在断言时),而 stub 不能。

Chai 是 Node.js 和浏览器的 BDD/TDD 断言框架。在前面的示例中,它与 Mocha 测试框架配对。

逐步测试核心布局

让我们分析一下CoreLayout.spec.js测试。该组件的角色类似于发布应用中的 CoreLayout,因此它是描述如何开始为应用编写测试的好方法。

CoreLayout 测试文件位置(2016 年 7 月)位于https://github.com/davezuko/react-redux-starter-kit/blob/master/tests/layouts/CoreLayout.spec.js

内容如下:

import React from 'react' 
import TestUtils from 'react-addons-test-utils' 
import CoreLayout from 'layouts/CoreLayout/CoreLayout' 

function shallowRender (component) { 
  const renderer = TestUtils.createRenderer() 

  renderer.render(component) 
  return renderer.getRenderOutput() 
} 

function shallowRenderWithProps (props = {}) { 
  return shallowRender(<CoreLayout {...props} />) 
} 

describe('(Layout) Core', function () { 
  let _component 
  let _props 
  let _child 

  beforeEach(function () { 
    _child = <h1 className='child'>Child</h1> 
    _props = { 
      children: _child 
    } 

    _component = shallowRenderWithProps(_props) 
  }) 

  it('Should render as a <div>.', function () { 
    expect(_component.type).to.equal('div') 
  }) 
})

react-addons-test-utils库使使用摩卡测试 React 成分变得简单。我们在前面的示例中使用的方法是**浅层渲染,**可在上获得 https://facebook.github.io/react/docs/test-utils.html#shallow-呈现

这个特性帮助我们测试render函数,并且是在我们的组件中呈现一个一级深度的结果。然后我们可以断言其render方法返回的内容,如下所示:

function shallowRender (component) { 
  const renderer = TestUtils.createRenderer() 

  renderer.render(component) 
  return renderer.getRenderOutput() 
} 

function shallowRenderWithProps (props = {}) { 
  return shallowRender(<CoreLayout {...props} />) 
}

首先,我们在shallowRender方法中提供一个组件(在本例中,它将是 CoreLayout)。稍后,我们使用method.render,然后使用renderer.getRenderOutput返回输出。

在我们的例子中,在这里调用该函数(请注意,在下面的示例中缺少分号,因为我们所描述的启动器具有与我们不同的 linting 选项):

describe('(Layout) Core', function () { 
  let _component 
  let _props 
  let _child 

  beforeEach(function () { 
    _child = <h1 className='child'>Child</h1> 
    _props = { 
      children: _child 
    } 

    _component = shallowRenderWithProps(_props) 
  }) 

  it('Should render as a <div>.', function () { 
    expect(_component.type).to.equal('div') 
  }) 
})

您可以发现_component变量包含renderer.getRenderOutput的结果。该值声明如下:

expect(_component.type).to.equal('div')

在该测试中,我们测试代码是否返回div。但如果您访问文档,则可以找到如下代码示例:

<div> 
  <span className="heading">Title</span> 
  <Subcomponent foo="bar" /> 
</div>

您还可以找到如下断言示例:

var renderer = ReactTestUtils.createRenderer(); 
result = renderer.getRenderOutput(); 
expect(result.type).toBe('div'); 
expect(result.props.children).toEqual([ 
  <span className="heading">Title</span>, 
  <Subcomponent foo="bar" /> 
]);

正如您在前面两个示例中所看到的,您可以预期类型为div,或者您可以预期有关 CoreLayout 返回的更具体的信息(取决于您的需要)。

第一个测试断言组件的类型(如果是div,第二个示例测试断言 CoreLayout 是否返回正确的组件,如下所示:

[ 
  <span className="heading">Title</span>, 
  <Subcomponent foo="bar" /> 
]

第一个是单元测试,因为这不是测试用户是否看到正确的东西。第二个是行为测试。

一般来说,Packt 有很多关于行为驱动开发BDD)和测试驱动开发TDD的书籍。

与特拉维斯的持续整合

在给定的示例中,您可以在找到一个.yml文件 https://github.com/davezuko/react-redux-starter-kit/blob/master/.travis.yml

这是 Travis 的配置文件。这是什么?它是一个托管 CI 服务,用于构建和测试软件。一般来说,它是一个开放源码项目可以免费使用的工具。如果您希望为私人项目提供托管的 Travis CI,则其费用适用。

Travis 的配置是通过添加.travis.yml文件完成的,如前所述。YAML 表单是一个文本文件,放置在项目的根目录中。该文件的内容描述了测试、安装和构建项目所必须完成的所有步骤。

Travis CI 的目标是向您的 GitHub 帐户提交所有内容并运行测试,当测试通过时,您可以将应用部署到 Amazon AWS 上的暂存服务器。持续集成不在本书的范围内,因此如果您有兴趣将此步骤添加到整个 publishing app 项目中,也有与此相关的书籍。

总结

我们的发布应用正在运行。与任何数字项目一样,我们仍有很多东西需要改进,以获得更好的最终产品。例如,以下作业是为您准备的:

  • 在前端添加一个迷你版,以便在通过 Internet 加载时更轻。
  • 如前所述,您需要开始使用 Karma 和 Mocha 进行单元测试和行为测试。本章详细介绍了一个示例设置。
  • 您需要选择一个 CI 工具,如 Travis,创建 YML 文件,并在 AWS 上准备环境。

除了本书 350 多页中介绍的内容之外,您还可以做的就是创建一个完整的堆栈 React+Redux+Falcor+Node+Express+Mongo 应用。我希望与你保持联系;在 Twitter/GitHub 上关注我,以便保持联系,如果您有任何其他问题,请向我发送电子邮件。

祝您在下一个商业全堆栈应用中好运,再见。