如何在Visual Studio Code中用Mocha對TypeScript進行測試

  首先,本文不是一篇介紹有關TypeScript、JavaScript或其它編程語言數據結構和算法的文章。如果你正在準備一場面試,或者學習某一個課程,互聯網上可以找到許多相關的資源,我個人比較推崇hackerrank.com

  本文的主要目的在於幫助你瞭解並熟知以下兩點:

  1. 如何用TypeScript編寫並運行測試用例?
  2. 如何在Visual Studio Code中使用調試器在線調試代碼,並發現代碼中的bug?

 

使用TypeScript編寫測試用例

  在我的電腦中,我創建了一個目錄ts-algorithms,在終端上運行下面的命令:

mkdir ts-algorithms
cd ts-algorithms
npm init -y

  這將創建一個最簡單的項目結構,其中包含一個package.json文件,你可以根據自己的需要隨意修改其中的內容,通常我會加入license、作者、描述和關鍵字等信息。

  接下來,我們編寫一些代碼,將其放在src目錄中,和測試相關的代碼則放在test目錄中。手動創建這兩個目錄,然後用TypeScript編寫一個模塊。例如,下面是我編寫的src/Stack.ts文件的代碼:

/**
 * Implementation of a classic stack.
 */
export class Stack<T> {
    // Internal storage for the stack

    private _items: T[] = [];

    /**
     * Creates a pre-populated stack.
     *
     * @param {T[]} initialData the initial contents of the stack
     */
    constructor(initialData: Array<T> = []) {
        this._items.push(...initialData)
    }

    /**
     * Adds an item to the top of the stack.
     *
     * @param {T} item the item to be added to the stack
     */
    push(item: T): void {
        this._items.push(item);
    }

    /**
     * Removes an item from the top of the stack, returning it.
     *
     * @returns {T} the item at the top of the stack.
     */
    pop(): T {
        return this._items.pop();
    }

    /**
     * Take a peek at the top of the stack, returning the top-most item,
     * without removing it.
     *
     * @returns {T} the item at the top of the stack.
     */
    peek(): T {
        if (this.isEmpty())
            return undefined;
        else
            return this._items[this._items.length - 1];

    }

    /**
     * @returns {boolean} true if the stack is empty.
     */
    isEmpty(): boolean {
        return this._items.length === 0;
    }

    /**
     * @returns {number} the number of items in the stack.
     */
    size(): number {
        return this._items.length;
    }
}

   同時我還在src目錄下添加了index.ts文件,用來導出所有的模塊。目前只有簡單的一行代碼:

export { Stack } from './Stack';

  爲了進行單元測試,我使用了mochachai這兩個包,我們通過下面的命令將它們安裝到項目裏:

npm install --save-dev mocha chai

  由於mocha不會自動運行TypeScript代碼,所以我們還需要用到ts-node

npm install --save-dev ts-node typescript

  另外,如果能讓Visual Studio Code編輯器理解mochachai的類型併爲其提供智能感知功能那就太好了,所以我們加上下面這兩個包:

npm install --save-dev @types/chai @types/mocha

  然後,我們在package.json中加入一個script的入口,這樣我們就可以通過一個命令行來運行我們的測試程序:

"scripts": {
  "tests": "mocha --require ts-node/register test/**/*.ts"
},

  注意這裏的--require ts-node/register很重要,它將TypeScript註冊爲解釋器,這樣我們就可以用TypeScript編寫測試用例了。

  讓我們創建文件test/stack.ts,編寫測試用例以對src/Stack.ts進行測試:

import { expect } from 'chai';
import { Stack } from '../src';

describe('Stack', () => {
    it('can be initialized without an initializer', () => {
        const s = new Stack<number>();
        expect(s.size()).to.equal(0);
    });
    it('can be initialized with an initializer', () => {
        const s = new Stack<number>([ 1, 2, 3, 4 ]);
        expect(s.size()).to.equal(4);
    });
    it('can be pushed upon', () => {
        const s = new Stack<number>([ 1, 2, 3, 4 ]);
        s.push(5);
        expect(s.size()).to.equal(5);
        expect(s.peek()).to.equal(5);
    });
    it('can be popped', () => {
        const s = new Stack<number>([ 1, 2, 3, 4 ]);
        expect(s.pop()).to.equal(4);
        expect(s.size()).to.equal(3);
    });
    it('can be peeked', () => {
        const s = new Stack<number>([ 1, 2, 3, 4 ]);
        expect(s.peek()).to.equal(4);
        expect(s.size()).to.equal(4);
    });
    it('isEmpty() returns true when empty', () => {
        const s = new Stack<number>();
        expect(s.isEmpty()).to.be.true;
    });
    it('isEmpty() is false when not empty', () => {
        const s = new Stack<number>([1, 2, 3, 41]);
        expect(s.isEmpty()).to.be.false;
    });
    it('cannot pop when no elements', () => {
        const s = new Stack<number>();
        expect(s.pop()).to.be.undefined;
    });
    it('cannot peek when empty', () => {
        const s = new Stack<number>();
        expect(s.peek()).to.be.undefined;
    });
});

  爲了確保TypeScript在編譯時通過類型檢查,我們還需要在項目的根目錄下添加tsconfig.json文件,並將mocha添加到types配置節點中:

{
    "compilerOptions": {
        "types": ["mocha"]
    }
}

  否則,直接運行測試,你可能會遇到下面這樣的錯誤:

TSError: ⨯ Unable to compile TypeScript:
test/stack.ts:4:1 - error TS2593: Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.

  所有工作準備就緒後,我們通過下面的命令直接運行測試:

npm run tests

  你也可以在Visual Studio Code的集成終端窗口中運行上面的命令(View > Terminal)。如果一切正常,你應該會得到如下圖所示的結果(所有9個測試用例都檢測成功):

   注意這裏所有的代碼,包括測試用例,都是用TypeScript編寫的。此外,藉助於Visual Studio Code編輯器,你還可以在編譯時進行類型檢查,泛型類型審查以及獲得對JavaScript所進行的一系列其它增強。

 

在Visual Studio Code中使用調試器在線調試代碼

  我們不僅僅只能運行測試,我們還可以做得更好。如果我們的測試用例在運行中遇到問題怎麼辦?例如,讓我們在src/Stack.ts文件中加入一行代碼,使isEmpty()方法始終報錯:

isEmpty(): boolean {
    throw new Error("Something went wrong");
    return this._items.length === 0;
}

  重新運行測試,首先失敗的就是第三個測試用例——"can be pushed upon"。我很想知道它爲什麼會失敗!在Visual Studio Code中打開Run and Debug

   然後左側的窗口變成下面這樣:

   在進行調試之前,我需要告訴Visual Studio Code如何在調試器中運行測試,點擊Run and Debug按鈕下方的create a launch.json file鏈接,然後在彈出的下拉列表中選擇Node.js環境。這將在.vscode目錄中創建一個launch.json配置文件,其中包含了一個默認的啓動配置項,我們將該配置項刪除,然後點擊界面右下角的Add Configuration按鈕,在彈出的列表中選擇{} Node.js: Mocha Tests,這是運行mocha測試的基本配置項。不過,這裏我們需要給mocha添加一些命令行參數,正如我們在package.json文件的script中向mocha添加的參數一樣。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "args": [
                "--require", "ts-node/register",
                "-u",
                "bdd",
                "--timeout",
                "999999",
                "--colors",
                "${workspaceFolder}/test/**/*.ts"
            ],
            "internalConsoleOptions": "openOnSessionStart",
            "name": "Mocha Tests",
            "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
            "request": "launch",
            "type": "pwa-node"
        }
    ]
}

  注意這裏的args配置項,它包含了一些非常重要的配置,其中之一就是我們在package.json文件的script中向mocha添加的命令行參數"--require"和"ts-node/register"。另外就是"${WorkspaceFolder}/test/**/*.ts"用來指定測試文件所在的目錄。還有一個就是我們將默認生成的配置項"tdd"改成"bdd"以確保mocha在Visual Studio Code的debugger模式下能正確運行。

  現在,我們可以在測試代碼中打斷點:

   然後,在Visual Studio Code的Run and Debug窗口中點擊綠色的運行按鈕:

   一旦斷點被命中,程序會暫停,然後你可以逐步跟蹤代碼執行:

   該控制條會顯示在屏幕的頂部,從左到右依次是:

  • 繼續執行,直到下一個斷點。
  • 執行到下一個語句。
  • 進入下一個語句內部。
  • 跳出當前語句。
  • 重新運行。
  • 停止運行。

  同時,Visual Studio Code的左側也有一些和調試相關的窗口,其中包含當前程序運行的變量的信息(通常會非常有用)、被監視的變量的值、調用堆棧以及斷點的位置信息等等。

  我們可以試着來調試一下。前兩個測試用例已經成功運行了,我們創建了stack類並且測試了它的size。在接下來的測試中我想要測試push()peek()這兩個方法。我們執行下面的操作:

  • 跳過stack類的創建。
  • 進入到push()方法內部。
  • 在push()方法內部跳過stack數組的push()調用。

  光標現在出現在push()方法的末尾,現在我可以查看一下變量的值——特別是this._items變量的值,因爲它表示的是stack類中items的數量。目前一切看起來都很正常,讓我們繼續:

  • 跳過push()方法的返回。
  • 然後我們可以直接跳過對size()方法的測試語句,因爲前面的測試用例已經測試過了。
  • 進入到peek()方法內部。
  • 進入到this.isEmpty()方法內部。
  • 然後我們看到程序拋出一個異常。

   如果想要暫時隱藏斷點使其在程序運行時不被命中,可以在左側的breakpoints窗口中點擊Toggle Activate Breakpoints。再次點擊時可以激活所有已添加的斷點。

  你可以通過Visual Studio Code提供的這種在線調試工具來一步步跟蹤執行我們的代碼,從而分析每個步驟中各個變量的值,看看它們是否和你預期的一致。你熟知你的算法,在編碼之前可以先在紙上將整個執行過程畫一遍,然後看看最終程序運行時是否偏離了你的預期?

  另外需要注意的是,在調試過程中,我們看到的是TypeScript代碼,而不是編譯之後的JavaScript代碼。

  現在我們可以使用Visual Studio Code來編寫更多豐富的功能強大的TypeScript代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章