الهدف في هذا المشروع هو إنشاء مشروع 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
وأريد إنشاء الإعلانات. لذلك، قمت بتكوين outDir
في tsconfig.json
ليكون ./lib
.
تم بعد ذلك تكوين .gitignore
الخاص بي ليكون:
/node_modules
/lib
...بينما .npmignore
الخاص بي هو فقط:
/node_modules
لنفس السبب، أقوم بإزالة القيمة الافتراضية files
الموجودة في tsconfig.json
واستبدالها بما يلي:
"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.config.js
لجميع إعدادات jest
الخاصة بي. يجب أن يأخذ هذا في الاعتبار حقيقة أنني أستخدم 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
لتجنب التحكم في الإصدار أو نشر دليل coverage
الذي تم إنشاؤه بواسطة jest
.
عند هذه النقطة، سأبدأ في تقديم وحدات فرعية في مشروعي. لذلك سأضيف وحدة 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 .
تذكر أننا أضفنا نصًا compile
إلى package.json
الخاص بنا. يمكننا تجميع الكود باستخدام yarn compile
. عند القيام بذلك، نرى أن دليل lib
مملوء بمجلدين فرعيين، src
و __tests__
.
ومع ذلك، إذا نظرنا إلى تلك الأدلة، فسنجد أنها تتضمن فقط كود Javascript الذي تم إنشاؤه. أنها لا تتضمن تعريفات النوع. من أجل إنشاء تعريفات النوع (ملفات .d.ts
) حتى يتمكن مستخدمو TypeScript الآخرون من الاستفادة من جميع معلومات النوع التي أضفناها إلى الكود الخاص بنا، يتعين علينا تعيين حقل declaration
في ملف tsconfig.json
ليكون true
.
لاحظ أيضًا أنه لكي يتمكن الآخرون من استخدام هذه الحزمة كوحدة NPM، يجب عليك ضبط الحقل main
في package.json
على lib/src/index.js
. علاوة على ذلك، لكي يتمكن الآخرون من الوصول إلى الأنواع الموجودة في هذه الوحدة، نحتاج أيضًا إلى ضبط حقل typings
في package.json
على 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، لذا سأوضح كيفية تشغيل تصحيح الأخطاء هناك. قد تُترجم بعض هذه المعلومات جيدًا إلى بيئات تطوير متكاملة أخرى.
في 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 لهذا الملف التمهيدي الذي يشرح الإصلاح).