在代码覆盖范围内使用Linting,Formatting和Unit Testing强制执行质量标准
从Adobe Stock Photo授权
如果要编写代码并将其交付生产,那么确保代码高质量是很重要的。
在上一篇文章中 ,我向您展示了如何使用docker-compose来利用标准化的,已经存在的Dockerfile进行开发。 使我们的应用程序为部署做好准备的下一步是对其进行生产。
我将继续使用先前教程中的React / Parcel示例: 移至Next.js和Webpack!
这是源代码: https : //github.com/patrickleet/streaming-ssr-react-styled-components
我也没有做任何其他与使应用程序“投产”有关的事情,因此我还将讨论这方面的要求,尽管可能还需要完成另一篇文章……我们将看看如何进行。 我正在搭便车。
让我们从一些质量控制开始。
在本文中,我们将探讨Linting,Formatting,Unit Testing和Code Coverage并执行一些质量标准。
整理和格式化
根据Wikipedia的说法,“ Lint或linter是一种用于分析源代码以标记编程错误,bug,样式错误和可疑结构的工具。”
这意味着它会强制执行诸如使用空格与制表符之类的操作,或者确保是否一致地使用分号来确保代码。
目前,我的项目中可能存在很多掉毛错误。
到目前为止,我的目标是演示特定的概念,而过多的关于不同事物的侧边栏确实使手头的概念无法使用。 因此,我选择放弃掉毛以保持先前的文章重点。
现在是时候“生产”我们的应用程序了,质量变得更加重要。
我首选的linter格式是StandardJS ,这是一个非常简单的设置。 但是在设置之前,我们还要谈一下格式化。
格式化与整理类似,但较少关注语法错误,而更关注仅使代码看起来更漂亮,因此是流行的程序包更漂亮的名称。
感谢Github上的几个很棒的开源贡献者,我们可以在一个更漂亮的标准包中同时使用它们。 感谢亚当·斯坦基维兹 , 肯特·多德斯 , 亚当·加勒特·哈里斯和贝诺瓦·阿弗蒂 !
在过去,我曾写过关于使用赫斯基确保每次提交之前都运行规则的信息。 还建议使用更漂亮的标准软件包,因此,现在就添加更漂亮的标准,沙哑和绒毛阶段。
配置更漂亮的标准
首先安装所需的软件包:
npm i --save-dev prettier-standard@ 9.1 .1 husky lint-staged
在package.json中,添加以下“格式”脚本以及新的“ lint-staged”和“ husky”部分:
{
//...
"scripts" : {
// ...
"format" : "prettier-standard 'app/**/*.js' 'app/**/*.jsx' 'server/**/*.js'"
},
"lint-staged" : {
"linters" : {
"**/*.js" : [
"prettier-standard" ,
"git add"
],
"**/*.jsx" : [
"prettier-standard" ,
"git add"
]
}
},
// ...
}
我无法让RegExp正常工作,因此如果不查看源代码,我会假设它使用的是glob而不是RegExp。
现在,您可以运行npm run format
格式化代码并检查棉绒错误。 另外,在您每次尝试提交时,都会调用husky的pre-commit
挂钩,这将确保在允许pre-commit
任何暂存文件( git add
stage文件)之前,将其正确插入。
让我们看看我在第一次通过时的表现。
➜ npm run format
> stream-all-the-things@ 1.0 .0 format /Users/patrick.scottgroup1001.com/dev/patrickleet/open-source-metarepo/stream-all-the-things
> prettier-standard 'app/**/*.js' 'app/**/*.jsx' 'server/**/*.js'
app/client.js 52 ms
app/imported.js 11 ms
app/styles.js 7 ms
app/App.jsx 11 ms
app/components/Header.jsx 76 ms
app/components/Page.jsx 7 ms
app/pages/About.jsx 6 ms
app/pages/ Error .jsx 5 ms
app/pages/Home.jsx 6 ms
app/pages/Loading.jsx 6 ms
server/index.js 8 ms
server/lib/client.js 11 ms
server/lib/ssr.js 17 ms
基本上,除了styles.js
之外,每个文件都有styles.js
错误或看起来不够漂亮!
忽略文件以进行格式化和格式化
这个项目特有一个小问题app/imported.js
是一个生成的文件,lint应该忽略它。
尽管在文件顶部已eslint-disabled
,但更漂亮的文件不知道强制执行linting规则。 不用担心,让我们撤消对该文件的更改,然后创建一个.prettierignore
文件和一个.eslintignore
文件以显式忽略它,以免在以后的运行中格式化。
git checkout -- ./app/imported.js
将撤消对该文件的更改。
现在,使用以下几行来创建.prettierignore
和.eslintignore
:
app/imported.js
dist
coverage
node_modules
现在,当运行npm run format
,文件app/imported.js
保持不变。 由于生成了文件,因此无法解决此问题。
最后,我提到提交也应该run npm run format
pre-commit
挂钩。 让我们尝试一下。
➜ git commit -m 'feat: prettier-standard'
husky > pre-commit (node v11 .6 .0 )
↓ Stashing changes... [skipped]
→ No partially staged files found...
✔ Running linters...
单元测试和代码覆盖率
作为生产应用程序的一部分,我们确实应该确保我们的代码已经过测试。 理想情况下,您应该一直这样做,但是我是一个坏人,到目前为止在这个项目中都忽略了它。
让我们解决这个问题。
安装和配置Jest
首先,让我们安装Jest来编写我们的单元测试。
npm i --save-dev jest babel-jest
接下来,让我们添加一个jest配置文件,以便我们配置jest以知道在哪里可以找到我们的文件并能够使用漂亮的路径。
添加以下jest.json
文件:
{
"roots" : [ "<rootDir>/__tests__/unit" ],
"modulePaths" : [
"<rootDir>" ,
"/node_modules/"
],
"moduleFileExtensions" : [
"js" ,
"jsx"
],
"transform" : {
"^.+\\.jsx?$" : "babel-jest"
},
"transformIgnorePatterns" : [ "/node_modules/" ],
"coverageThreshold" : {
"global" : {
"branches" : 10 ,
"functions" : 10 ,
"lines" : 10 ,
"statements" : 10
}
},
"collectCoverage" : true ,
"collectCoverageFrom" : [
"**/*.{js,jsx}"
]
}
好吧,让我们打开包装。 首先,我们将roots
设置为<rootDir>/__tests__/unit
。 我喜欢将暂存测试放在__tests__/staging
因此将根设置为__tests__/unit
将允许我稍后进行此操作。
接下来,我们将modulePaths
设置为根目录,并设置node_modules
。 在我们的测试中,这种方式无需使用../../
等相对路径,我们只需导入app/*
或server/*
。
接下来的两个键告诉Jest使用babel加载文件,这样import
东西就可以正常工作。
最后,最后三个部分定义了覆盖范围设置-最低阈值(均为10%)以及从何处收集覆盖范围。 在本文中,我只是旨在配置各个部分。 在下一个中,我将覆盖率阈值提高到100%,并逐步完成该过程。
为了运行,我们可以在package.json
的scripts部分中定义一个test
脚本。 因为我们正在使用babel-jest,所以我们还需要提供一些babel设置,所以我们可以将BABEL_ENV
设置为test
,我们将在下一部分中解决。
"scripts" : {
// ...
"test" : "cross-env BABEL_ENV=test jest --config jest.json" ,
"test:watch" : "cross-env BABEL_ENV=test jest --config jest.json --watch"
}
用Babel配置Jest
首先,为了使测试正常进行,我们需要配置一些babel设置。 在.babelrc文件的env密钥中添加以下部分:
{
"env" : {
"test" : {
"presets" :[
[ "@babel/preset-env" ],
[ "@babel/preset-react" ],
],
"plugins" : [
[ "@babel/plugin-syntax-dynamic-import" ]
]
},
// ...
}
}
然后安装我们参考的插件和预设:
npm i --save-dev @babel/core @babel/preset-env @babel/preset-react @babel/plugin-syntax-dynamic- import babel-jest
目前我们的覆盖率为0%,让我们为该应用添加一项测试,为服务器添加一项测试,这应该使我们超过10%的下限阈值。
使用酶测试客户端应用程序
首先,让我们在应用程序中测试文件。 我们将要在应用程序中浅化渲染组件以对其进行测试。 为此,我们将使用enzyme
。
npm i --save-dev enzyme enzyme-adapter-react -16
酶有一个设置步骤,我们必须先添加该步骤,然后才能在测试中使用它。 在我们的jest.json
文件中,添加一个新密钥:
{
//other settings
"setupTestFrameworkScriptFile" : "<rootDir>/__tests__/setup.js"
}
设置文件位于__tests__/unit/setup.js
:
import { configure } from 'enzyme' ;
import Adapter from 'enzyme-adapter-react-16' ;
configure({ adapter : new Adapter() });
现在配置了酶之后,我们可以创建__tests__/unit/app/pages/Home.jsx
:
似乎我们的组件只是一个功能,这就是我们需要达到此文件的100%覆盖率的全部。
服务器端测试
import React from 'react'
import { shallow } from 'enzyme'
import Home from 'app/pages/Home.jsx'
describe( 'app/pages/Home.jsx' , () => {
it( 'renders style component' , () => {
expect(Home).toBeDefined()
const tree = shallow( < Home /> )
expect(tree.find('Page')).toBeDefined()
expect(tree.find('Helmet').find('title').text()).toEqual('Home Page')
expect(tree.find('div').text()).toEqual('Follow me at @patrickleet')
expect(tree.find('div').find('a').text()).toEqual('@patrickleet')
})
})
我想测试server/index.js
但在此之前,有一些重构可以使我们的生活更轻松一些。
单元测试旨在测试单个单元。 这意味着即使我们的应用使用的是Express,我们也不会在此单元测试中测试Express。 我们正在测试是否为服务器配置了适当的路由,中间件,并调用了listen方法来启动服务器。 Express的单元测试属于Express项目。
为了仅测试我们关心的单个单元,我们可以使用模拟来创建轻量级接口,可以使用jest.mock进行跟踪。 如果我们从索引中提取服务器实例到它自己的文件中,我们将能够更轻松地模拟服务器。
使用以下内容创建文件server/lib/server.js
:
import express from 'express'
export const server = express()
export const serveStatic = express.static
像这样更新server / index.js:
import path from 'path'
import log from 'llog'
import { server, serveStatic } from './lib/server'
import ssr from './lib/ssr'
// Expose the public directory as /dist and point to the browser version
server.use(
'/dist/client' ,
serveStatic(path.resolve(process.cwd(), 'dist' , 'client' ))
)
// Anything unresolved is serving the application and let
// react-router do the routing!
server.get( '/*' , ssr)
// Check for PORT environment variable, otherwise fallback on Parcel default port
const port = process.env.PORT || 1234
server.listen(port, () => {
log.info( `Listening on port ${port} ...` )
})
现在在我们的测试中,我们可以简单地模拟server/lib/server.js
而不是更复杂的express模拟。
让我们在__tests__/unit/server/index.js
上创建测试:
import 'server/index'
jest.mock( 'llog' )
jest.mock( 'server/lib/server' , () => ({
server : {
use : jest.fn(),
get : jest.fn(),
listen : jest.fn()
},
serveStatic : jest.fn( () => "static/path" )
}))
jest.mock( 'server/lib/ssr' )
describe( 'server/index.js' , () => {
it( 'main' , () => {
const { server, serveStatic } = require ( 'server/lib/server' )
expect(server.use).toBeCalledWith( '/dist/client' , "static/path" )
expect(serveStatic).toBeCalledWith( ` ${process.cwd()} /dist/client` )
expect(server.get).toBeCalledWith( '/*' , expect.any( Function ))
expect(server.listen).toBeCalledWith( 1234 , expect.any( Function ))
})
})
如果现在运行覆盖率,我们将注意到server/index.js
的覆盖率不是100%。 我们传递了一个匿名函数来监听,很难做到。 这需要一些小的重构。
重构监听调用以提取匿名函数。
export const onListen = port => () => {
log.info( `Listening on port ${port} ...` )
}
server.listen(port, onListen(port))
现在我们可以轻松地测试onListen
。
让我们在server/index.js
套件中添加另一个测试来解决这个问题。
import { onListen } from 'server/index'
// ...
describe( 'server/index.js' , () => {
// ...
it( 'onListen' , () => {
const log = require ( 'llog' )
onListen( 4000 )()
expect(log.info).toBeCalledWith( 'Listening on port 4000...' )
})
})
这样一来,我们就可以100%覆盖server/index.js
以及app/pages/Home.jsx
。
通过我们的两项测试,我们成功地将覆盖范围从0%增加到了35–60%,具体取决于指标:
目前的覆盖范围
将测试添加到预提交的挂钩
最后,我们只希望确保将测试作为预提交挂钩,以防止将损坏的测试纳入代码,以及以后的所有未经测试的代码。
在package.json
pre-commit
更改为:
"pre-commit" : "lint-staged && npm run test"
现在,当有人尝试提交该项目时,它将强制执行您在Jest配置中定义的coverage标准,并确保所有测试都通过!
结论
当需要准备好应用程序产品时,必须以自动化方式强制执行质量标准。 在本文中,我向您展示了如何设置和配置用于整理和格式化代码的工具,以及如何配置您的项目以使用酶和开玩笑在强制性代码覆盖率下进行测试。
在下一部分中,在继续创建可用于生产的Dockerfile之前,我们将覆盖率提高到100%。
与往常一样,如果您发现这有帮助,请单击并按住鼓掌按钮最多50个鼓掌,关注我并与他人分享!
最好,
帕特里克·李·斯科特
查看本系列的其他文章! 这是第3部分。
From: https://hackernoon.com/enforcing-code-quality-for-node-js-c3b837d7ae17