這個專案的目標是建立一個可以執行以下所有操作的 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 "
}
此時,如果我執行ya 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 來解釋該修復的自述文件)。