我们成功了;祝贺我们已经创建了一个在某个域名下运行的全栈应用(在本书中是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:*"
}
}内容(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 上关注我,以便保持联系,如果您有任何其他问题,请向我发送电子邮件。
祝您在下一个商业全堆栈应用中好运,再见。