Das Ziel dieses Projekts besteht darin, ein TypeScript-Projekt zu erstellen, das Folgendes tun kann:
jest
und ts-jest
zum Testen Ich beginne damit, dies als npm
-Projekt zu initialisieren.
$ yarn init .
Dann installiere typescript
, jest
, ts-jest
und @types/jest
als Abhängigkeiten:
$ yarn add -D typescript jest ts-jest @types/jest
Zum Zeitpunkt des Verfassens dieses Artikels bedeutet das [email protected]
, [email protected]
und [email protected]
.
Als nächstes initialisieren wir dies als TypeScript-Projekt mit:
$ npx tsc --init .
Ich möchte, dass mein von TypeScript generierter Code in ./lib
gespeichert wird und dass Deklarationen generiert werden. Also konfiguriere ich outDir
in tsconfig.json
als ./lib
.
Mein .gitignore
ist dann wie folgt konfiguriert:
/node_modules
/lib
...während mein .npmignore
nur ist:
/node_modules
Aus dem gleichen Grund entferne ich den Standardwert für files
in tsconfig.json
und ersetze ihn durch:
"exclude" : [ " node_modules " , " lib " ]
Zu Beginn erstelle ich eine src/index.ts
, die eine einfache Funktion enthält:
export function sampleFunction ( x : string ) : string {
return x + x ;
}
Ich füge auch einen einfachen jest
hinzu. Ich bevorzuge es, meine Tests an einem völlig separaten Ort aufzubewahren, daher lege ich alle meine Tests in __tests__
ab. Also erstelle ich den folgenden Testfall in __tests__/base.spec.ts
:
import { sampleFunction } from "../src" ;
describe ( "This is a simple test" , ( ) => {
test ( "Check the sampleFunction function" , ( ) => {
expect ( sampleFunction ( "hello" ) ) . toEqual ( "hellohello" ) ;
} ) ;
} ) ;
An diesem Punkt möchte ich diesen Test durchführen. Aber zuerst muss ich eine jest.config.js
Datei für alle meine jest
-Einstellungen erstellen. Dies muss die Tatsache berücksichtigen, dass ich ts-jest
verwende und die Tatsache, dass meine Tests in __tests__
gespeichert sind. Die resultierende Datei sieht also so aus:
module . exports = {
transform : {
"^.+\.tsx?$" : "ts-jest" ,
} ,
testRegex : "(/__tests__/.*|(\.|/)(test|spec))\.(jsx?|tsx?)$" ,
moduleFileExtensions : [ "ts" , "tsx" , "js" , "jsx" , "json" , "node" ] ,
} ;
Anschließend füge ich die folgenden Skripte zu package.json
hinzu:
"scripts" : {
"compile" : " tsc " ,
"test" : " jest "
}
Wenn ich jetzt yarn test
ausführe, erhalte ich genau das, was ich mir erhofft habe:
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
Um die Codeabdeckung zu aktivieren, aktualisiere ich meine jest.config.js
Datei auf:
module . exports = {
transform : {
"^.+\.tsx?$" : "ts-jest" ,
} ,
testRegex : "(/__tests__/.*|(\.|/)(test|spec))\.(jsx?|tsx?)$" ,
moduleFileExtensions : [ "ts" , "tsx" , "js" , "jsx" , "json" , "node" ] ,
collectCoverage : true ,
} ;
Ich möchte auch meine .gitignore
und .npmignore
Dateien aktualisieren, um eine Versionskontrolle oder Veröffentlichung des von jest
generierten coverage
Verzeichnisses zu vermeiden.
An diesem Punkt werde ich mit der Einführung von Untermodulen in meinem Projekt beginnen. Deshalb füge ich ein src/core
und ein src/utils
-Modul hinzu, um die Dinge etwas realistischer zu machen. Dann exportiere ich den Inhalt beider, sodass src/index.ts
so aussieht:
export * from "./core" ;
export * from "./utils" ;
Diese importieren dann bestimmte Dateien mit unterschiedlichen Typen und Funktionen. Zunächst erstelle ich einen sehr einfachen Satz von Typen zur Darstellung extrem einfacher Ausdrücke nur mit Literalen und den binären Operationen +
, -
, *
und /
. Dann kann ich ein paar Tests wie diese schreiben:
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 ) ;
} ) ;
} ) ;
So weit, ist es gut. Beachten Sie jedoch, dass ich folgende Ergebnisse erhalte, wenn ich diese Tests tatsächlich durchführe:
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 | |
---------------|----------|----------|----------|----------|----------------|
Beachten Sie die mangelnde Codeabdeckung. Wenn wir ein paar weitere Testfälle zusammen mit einigen /* istanbul ignore ... */
Kommentaren hinzufügen, um istanbul
wissen zu lassen, was es sicher ignorieren kann, kommen wir zu:
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 | |
---------------|----------|----------|----------|----------|----------------|
Wenn wir nun einen Test so ändern, dass er fehlschlägt, erhalten wir etwa Folgendes:
● 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 | |
---------------|----------|----------|----------|----------|----------------|
Beachten Sie, dass die Stapelspur korrekt ist. Es weist auf das Problem im TypeScript-Code hin.
Denken Sie daran, dass wir unserer package.json
ein compile
hinzugefügt haben. Wir können den Code mit yarn compile
kompilieren. Dabei sehen wir, dass das lib
-Verzeichnis mit zwei Unterverzeichnissen gefüllt ist, src
und __tests__
.
Wenn wir jedoch in diese Verzeichnisse schauen, werden wir feststellen, dass sie nur den generierten Javascript-Code enthalten. Sie enthalten keine Typdefinitionen. Um Typdefinitionen ( .d.ts
-Dateien) zu generieren, damit andere TypeScript-Benutzer von allen Typinformationen profitieren können, die wir unserem Code hinzugefügt haben, müssen wir das declaration
in unserer tsconfig.json
Datei auf true
setzen.
Beachten Sie außerdem, dass Sie das main
in package.json
auf lib/src/index.js
setzen müssen, damit andere dieses Paket als NPM-Modul verwenden können. Damit andere außerdem auf die Typen in diesem Modul zugreifen können, müssen wir außerdem das Feld typings
in package.json
auf lib/src/index.d.ts
setzen. Mit anderen Worten,
"main" : " lib/src/index.js " ,
"typings" : " lib/src/index.d.ts " ,
Bei richtiger Konfiguration können wir dann eine node
starten und unser neues Paket importieren:
$ node
> var me = require( " . " )
undefined
> me
{ evaluate: [Function: evaluate],
assertNever: [Function: assertNever] }
>
Stellen Sie nun sicher, dass Sie Ihre jest.config.js
so aktualisieren, dass sie die folgende Einstellung enthält, sonst beginnt jest
mit dem Abgleich des Codes im Verzeichnis lib/__tests__
:
testPathIgnorePatterns: ["/lib/", "/node_modules/"],
Zum Schluss kommen wir zum Debuggen. Ich verwende Visual Studio Code und zeige daher, wie ich das Debuggen dort zum Laufen bringe. Einige dieser Informationen lassen sich möglicherweise auch auf andere IDEs übertragen.
In VSCode können wir zur Debug-Seitenleiste gehen. Zunächst wird neben der Schaltfläche „Play“ die Aufschrift „No Configuration“ angezeigt. Wenn Sie darauf klicken, wird ein Pulldown-Menü mit der Option „Konfiguration hinzufügen …“ angezeigt.
So sehr ich TypeScript auch liebe, das Debuggen ist wirklich seine Achillesferse. Es ist nicht so, dass man nicht debuggen kann, es ist einfach nur schwierig, es zum Laufen zu bringen. Wenn Sie „Konfiguration hinzufügen…“ und dann „Node.js“ auswählen, werden mehrere Vorkonfigurationen angezeigt, darunter eine für mocha
. Aber es gibt keinen für jest
. Sie müssen also Ihre eigene .vscode/launch.json
Datei erstellen. Glücklicherweise schlägt Ihnen die jest
vor, eine .vscode/launch.json
Datei zu erstellen, die so aussieht:
{
"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 "
}
]
}
Ich war angenehm überrascht, als ich feststellte, dass ich nicht nur wie gewohnt meine Tests ausführen und Codeabdeckung erhalten konnte, sondern auch Haltepunkte sowohl in den Tests ( z. B. in __tests__/base.spec.ts
) als auch im Code ( z. B. src/core/functions.ts
) und der Debugger wird sie finden.
Beachten Sie, dass ich dies alles auf Knoten 8.x getestet habe. Ich habe Probleme beim Debuggen mit Node 6.x gesehen. Wenn Sie also Probleme damit haben, sollten Sie ein Upgrade in Betracht ziehen (oder lassen Sie, wenn Sie es schaffen, das Problem zu beheben, eine PR für diese README-Datei einreichen, in der das Problem erläutert wird).