该项目的目标是创建一个可以执行以下所有操作的 TypeScript 项目:
jest
和ts-jest
进行测试我首先将其初始化为npm
项目。
$ yarn init .
然后,我安装typescript
、 jest
、 ts-jest
和@types/jest
作为依赖项:
$ yarn add -D typescript jest ts-jest @types/jest
在撰写本文时,这意味着[email protected]
、 [email protected]
和[email protected]
。
接下来,我们使用以下命令将其初始化为 TypeScript 项目:
$ npx tsc --init .
我希望 TypeScript 生成的代码存储在./lib
中,并且我希望生成声明。因此,我将tsconfig.json
中的outDir
配置为./lib
。
然后我的.gitignore
配置为:
/node_modules
/lib
...而我的.npmignore
只是:
/node_modules
出于同样的原因,我删除了tsconfig.json
中files
的默认值并将其替换为:
"exclude" : [ " node_modules " , " lib " ]
首先,我创建一个src/index.ts
,其中包含一个简单的函数:
export function sampleFunction ( x : string ) : string {
return x + x ;
}
我还添加了一个简单的jest
测试。我更喜欢将测试放在完全独立的位置,因此我将所有测试放在__tests__
中。因此,我在__tests__/base.spec.ts
中创建以下测试用例:
import { sampleFunction } from "../src" ;
describe ( "This is a simple test" , ( ) => {
test ( "Check the sampleFunction function" , ( ) => {
expect ( sampleFunction ( "hello" ) ) . toEqual ( "hellohello" ) ;
} ) ;
} ) ;
此时,我想运行该测试。但首先我需要为所有jest
设置创建一个jest.config.js
文件。这必须考虑到我正在使用ts-jest
以及我的测试存储在__tests__
中的事实。所以生成的文件如下所示:
module . exports = {
transform : {
"^.+\.tsx?$" : "ts-jest" ,
} ,
testRegex : "(/__tests__/.*|(\.|/)(test|spec))\.(jsx?|tsx?)$" ,
moduleFileExtensions : [ "ts" , "tsx" , "js" , "jsx" , "json" , "node" ] ,
} ;
然后,我将以下脚本添加到package.json
:
"scripts" : {
"compile" : " tsc " ,
"test" : " jest "
}
此时,如果我运行yarn test
,我就会得到我所希望的结果:
PASS __tests__/base.spec.ts
This is a simple test
✓ Check the sampleFunction function (3ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
为了启用代码覆盖率,我将jest.config.js
文件更新为:
module . exports = {
transform : {
"^.+\.tsx?$" : "ts-jest" ,
} ,
testRegex : "(/__tests__/.*|(\.|/)(test|spec))\.(jsx?|tsx?)$" ,
moduleFileExtensions : [ "ts" , "tsx" , "js" , "jsx" , "json" , "node" ] ,
collectCoverage : true ,
} ;
我还想更新我的.gitignore
和.npmignore
文件以避免版本控制或发布jest
生成的coverage
目录。
现在,我将开始在我的项目中引入子模块。因此,我将添加一个src/core
和一个src/utils
模块,以使事情变得更加现实。然后我将导出这两个内容,以便src/index.ts
看起来像这样:
export * from "./core" ;
export * from "./utils" ;
然后导入包含各种类型和函数的特定文件。最初,我将创建一组非常简单的类型,用于表示仅包含文字和二进制运算+
、 -
、 *
和/
极其简单的表达式。然后我可以编写一些这样的测试:
import { evaluate , Expression } from "../src" ;
describe ( "Simple expression tests" , ( ) => {
test ( "Check literal value" , ( ) => {
expect ( evaluate ( { type : "literal" , value : 5 } ) ) . toBeCloseTo ( 5 ) ;
} ) ;
test ( "Check addition" , ( ) => {
let expr : Expression = {
type : "binary" ,
operator : "+" ,
left : {
type : "literal" ,
value : 5 ,
} ,
right : {
type : "literal" ,
value : 10 ,
} ,
} ;
expect ( evaluate ( expr ) ) . toBeCloseTo ( 15 ) ;
} ) ;
} ) ;
到目前为止,一切都很好。但请注意,如果我实际运行这些测试,我会得到以下结果:
PASS __tests__/base.spec.ts
Simple expression tests
✓ Check literal value (4ms)
✓ Check addition
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 2.048s
Ran all test suites.
---------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
---------------|----------|----------|----------|----------|----------------|
All files | 66.67 | 37.5 | 50 | 66.67 | |
src | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
src/core | 61.54 | 37.5 | 100 | 61.54 | |
functions.ts | 54.55 | 37.5 | 100 | 54.55 | 14,16,18,20,25 |
index.ts | 100 | 100 | 100 | 100 | |
src/utils | 66.67 | 100 | 0 | 66.67 | |
checks.ts | 50 | 100 | 0 | 50 | 2 |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|----------------|
请注意缺乏代码覆盖率。添加更多测试用例以及一些/* istanbul ignore ... */
注释,让istanbul
知道它可以安全地忽略什么,我们得到:
PASS __tests__/base.spec.ts
Simple expression tests
✓ Check literal value (3ms)
✓ Check addition
✓ Check subtraction
✓ Check multiplication (1ms)
✓ Check division
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 1.353s
Ran all test suites.
---------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
---------------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
src | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
src/core | 100 | 100 | 100 | 100 | |
functions.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
src/utils | 100 | 100 | 100 | 100 | |
checks.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|----------------|
现在,如果我们更改测试以使其失败,我们会得到如下结果:
● Simple expression tests › Check division
expect(received).toBeCloseTo(expected, precision)
Expected value to be close to (with 2-digit precision):
1
Received:
2
19 | test("Check division", () => {
20 | let expr = bin("/", 10, 5);
> 21 | expect(evaluate(expr)).toBeCloseTo(1);
22 | });
23 | });
24 |
at Object.<anonymous> (__tests__/base.spec.ts:21:32)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 4 passed, 5 total
Snapshots: 0 total
Time: 1.535s
Ran all test suites.
---------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
---------------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
src | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
src/core | 100 | 100 | 100 | 100 | |
functions.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
src/utils | 100 | 100 | 100 | 100 | |
checks.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|----------------|
请注意,堆栈轨迹是正确的。它指出了TypeScript 代码中的问题。
回想一下,我们在package.json
中添加了一个compile
脚本。我们可以使用yarn compile
来编译代码。这样做,我们看到lib
目录填充了两个子目录: src
和__tests__
。
然而,如果我们查看这些目录,我们会发现它们只包含生成的 Javascript 代码。它们不包括类型定义。为了生成类型定义( .d.ts
文件),以便其他 TypeScript 用户可以从我们添加到代码中的所有类型信息中受益,我们必须将tsconfig.json
文件中的declaration
字段设置为true
。
另请注意,为了让其他人将此包用作 NPM 模块,您需要将package.json
中的main
字段设置为lib/src/index.js
。此外,为了让其他人能够访问该模块中的类型,我们还需要将package.json
中的typings
字段设置为lib/src/index.d.ts
。换句话说,
"main" : " lib/src/index.js " ,
"typings" : " lib/src/index.d.ts " ,
如果配置正确,我们就可以启动node
会话并导入新包:
$ node
> var me = require( " . " )
undefined
> me
{ evaluate: [Function: evaluate],
assertNever: [Function: assertNever] }
>
现在请务必更新jest.config.js
以包含以下设置,否则jest
将开始匹配lib/__tests__
目录中的代码:
testPathIgnorePatterns: ["/lib/", "/node_modules/"],
最后,我们来进行调试。我使用的是 Visual Studio Code,因此我将演示如何在那里进行调试。其中一些信息可以很好地转化为其他 IDE。
在VSCode中,我们可以进入调试侧栏。最初,“播放”按钮旁边将显示“无配置”字样。单击该按钮会弹出一个下拉菜单,其中包含“添加配置...”选项。
尽管我很喜欢 TypeScript,但调试确实是它的致命弱点。并不是说你不能调试,而是很难开始工作。如果您选择“添加配置...”,然后选择“Node.js”,您将看到多个预配置,其中包括mocha
的预配置。但没有一个可以jest
的。因此,您必须创建自己的.vscode/launch.json
文件。幸运的是, jest
页面建议您创建一个如下所示的.vscode/launch.json
文件:
{
"version" : " 0.2.0 " ,
"configurations" : [
{
"name" : " Debug Jest Tests " ,
"type" : " node " ,
"request" : " launch " ,
"runtimeArgs" : [ " --inspect-brk " , " ${workspaceRoot}/node_modules/.bin/jest " , " --runInBand " ],
"console" : " integratedTerminal " ,
"internalConsoleOptions" : " neverOpen "
}
]
}
我惊喜地发现我不仅可以像往常一样运行测试并获得代码覆盖率,而且还可以在测试(即在__tests__/base.spec.ts
)以及代码中设置断点(例如, src/core/functions.ts
),调试器会找到它们。
请注意,我在 Node 8.x 上测试了所有这些。我已经看到使用 Node 6.x 进行调试的问题,因此如果您在那里遇到问题,您可能会考虑升级(或者,如果您设法修复它,请提交一个 PR 来解释该修复的自述文件)。