最新。頭版

  • 本文永久連結: https://dwatow.github.io/2019/08-14-angularjs/angular-jest/
  • AngularJS + Jest 實戰

    1. AngularJS + Jest 實戰
      1. 測試案例
      2. 測試初始畫面
        1. 測試目標
        2. 建立測試程式
        3. 寫測試
      3. 可能的問題: Jest 的 loader
      4. 測試互動改畫面
        1. 修改畫面
        2. 寫測試
      5. 測試 service 傳給 component 的值
        1. 改 component
        2. 寫測試
      6. 結論

    AngularJS + Jest 實戰

    實做專案: https://github.com/dwatow/AngularJS-demo/tree/jest-demo

    一開始,先 git clone repository

    git@github.com:dwatow/AngularJS-demo.git

    測試案例

    1. 顯示變數內容到畫面
    2. 使用 button 改變變數,同時修改畫面

    測試初始畫面

    有兩個東西可以測

    1. binding 進 component 的值
    2. $http 透過 API 得到的值

    binding 進來的值較單純,$http 等到測非同步時再來說。

    測試目標

    component 在註冊時,binding 是這樣寫的

    HelloWorld/index.html

    1
    2
    3
    <div class="hello">
    <h1>{{ vm.msg }}</h1>
    ...

    HelloWorld/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class controller {
    constructor() {
    // ...
    }
    }

    export default angular.module("helloWorld", []).component("helloWorld", {
    template,
    controller,
    bindings: {
    msg: "<",
    },
    controllerAs: "vm",
    }).name;

    在 App 的使用 helloWorld 方式是這樣

    App/index.html

    <hello-world msg="'Welcome to Your AngularJS App'"></hello-world>

    建立測試程式

    為了簡化 AngularJS 框架初始在測試程式的程式碼,使用 angularjs-jest [1]

    宣告 AngularJS 和 AngularJS 的 mock 函數[2]

    test/helloWorld.spec.js

    1
    2
    import angular from 'angular';
    import 'angular-mocks';

    引入要用測試的 module

    3
    4
    import mathservice from '../src/service/mathmodule.js';
    import helloWorld from '../src/components/HelloWorld/index.js';

    引入要用測試的 angularjs-jest

    5
    import { createTestApp } from 'angularjs-jest';

    引入能在 node 跑類似 jQuery 的功能

    6
    import cheerio from 'cheerio';

    用 angularjs-jest 初始化。並且將 $componentController inject 進測試 app 中。它可以讓我們得到 component 的實體。(不包含 template)

    7
    8
    9
    10
    11
    12
    describe('component: HelloWorld', function () {
    const getTestApp = () => createTestApp({
    modules: [helloWorld, mathservice],
    access: ['$componentController'],
    });
    //...

    寫測試

    13
    14
    15
    //...
    it('initial render binding msg: should be Welcome to Your AngularJS App', () => {
    const app = getTestApp();

    直接取得渲染結果,並且將結果載入 cheerio 中。
    並且測試 h1 內的文字是否與 binding 的字是一樣的。

    16
    17
    18
    const element = app.render(`<hello-world msg="'Welcome to Your AngularJS App'"></hello-world>`)
    const $ = cheerio.load(element.html())
    expect($('h1').text()).toBe("Welcome to Your AngularJS App");

    取得 controller 程式執行。並且同時設定 binding。
    測試 vm.msg 內的文字是否與 binding 的字是一樣的。

    19
    20
    21
    22
    23
      const vm = app.$componentController(helloWorld, null, {
    msg: "Welcome to Your AngularJS App",
    });
    expect(vm.msg).toEqual("Welcome to Your AngularJS App");
    });

    可能的問題: Jest 的 loader

    因為 Jest 只吃 JavaScript (和 Webpack 一樣) 所以,有時可以拿 webpack 的 loader 來使用。在此打算使用 html-loader 來載入 html 檔。

    安裝 html-loader-jest 較方便

    npm install -D html-loader-jest

    在 package.json 設定 jest 區段
    (這是可以設定 Jest 的眾多方法其中之一)

    package.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    //...
    "jest": {
    // ...
    "transform": {
    "^.+\\.(js|jsx)$": "babel-jest",
    "^.+\\.html?$": "html-loader-jest",
    // ...
    }
    }

    這樣就可以將載入的 html 檔轉成 JavaScript 的字串。

    測試互動改畫面

    修改畫面

    目前的測試程式是仿照 vue-cli 產生的畫面。它滿足了第一個測試案例,將變數顯示到畫面上。而第二個測試案例「使用 input」必須要先修改一下,才可以

    HelloWorld/index.html

    <div class="hello">
    <h1>{{ vm.msg }}</h1>
    <p>
    <input type="button" value="change head 1"
    ng-click="vm.changeHead('Chris')">
    </p>

    HelloWorld/index.js

    class controller {
    //...
    changeHead(msg) {
    this.msg = msg;
    }
    }

    寫測試

    先將初始字串印出來。

    test/helloWorld.spec.js

    1
    2
    3
    4
    5
    it('hello world click button change text to Chris', () => {
    const vm = app.$componentController(helloWorld, null, {
    msg: "Welcome to Your AngularJS App",
    });
    expect(vm.msg).toBe("Welcome to Your AngularJS App");

    再執行 changeHead()ng-click 綁定的 function
    再測試一次一樣的位置,顯示的內容要改變

    6
    7
    8
      vm.changeHead('Chris')
    expect(vm.msg).toBe("Chris");
    });

    要自行執行 event callback[3]

    測試 service 傳給 component 的值

    這一部份就是純邏輯的測試,非常適合 unit test

    改 component

    HelloWorld/index.js

    加上一個

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class controller {
    constructor($log, mathservice) {
    this.$log = $log;
    this.mathservice = mathservice;
    }

    $onInit() {
    this.math = this.mathservice.addTwoNumbers(1, 2);
    this.$log.log(this.math);
    }
    }

    寫測試

    在取得 componentController 之後,

    test/helloWorld.spec.js

    1
    2
    3
    4
    5
    it('initial render service data', () => {
    const vm = app.$componentController(helloWorld);
    vm.$onInit();
    expect(vm.math).toBe(3);
    });

    要自行執行 lifecycle

    結論

    使用 Jest 測試 AngularJS

    • 沒有一個好的方式可以觸發 event 。
      只能直接操作 component 的 event callback 假裝操作了 event。
    • 不能自動執行 component 的 lifecycle [4][5]

    angularjs-jest 的 render 功能

    • 無法再操作 dom 進行 event 的 trigger。會出現 trigger is not a function 的錯誤。也許是因為執行在 node.js 上面。
      不過對於 snapshot 測試初始渲染的來說是一件非常方便的事。因為它會自動執行 $onInit 的結果。

    下次再來試,怎麼測試 API 傳送的 case


    1. AngularJS and Jest. Three steps to improve your legacy frontend tests, 1. Throw away the cluttered test setup boilerplate ↩︎

    2. ngMock - AngularJS ↩︎

    3. Unit-testing Component Controllers ↩︎

    4. Angular’s $componentController ↩︎

    5. PCHR-3944: Update to AngularJS v1.7 #2790, Use of $onInit hook in components ↩︎

  • tags:
  • { jest }
  • { angularjs }

標籤雲

2018鐵人賽 API Doc Aglio Block Box Model CRUD CSS CSS Unit CSS3 Flex HTML Inline JavaScript Media Query Migration Position Pseudo-elements RWD Render tree Rulest Order Selector Sequelize Specificity TED Transition Vendor Prefix angularjs animation api awk axios babel background bash body bootstrap border canvas chatbot cloud fonts codepen css-loader define display docker docsify e2e env eslint express facebook file api file-loader filter flex-grow flex-shrink fontawesome foreign key git git reset git-ftp gitalk google fonts gulp hackmd heroku hexo http https husky import iview ivuew jest jquery json keyframes login mac markdown markdown-it markdown測試 mixin monent nginx ngork node-sass nodejs npm oo opacity outline pm2 prettier protractor pure component sequelize shell ssl transition unpkg v-model viewport visibility vue-cli vue-loader vue-masonry vue-router vue-slot vuejs vuex vuex' webpack webpack loader which 你所不知道的js 動畫特效 團隊溝通技巧 好想工作室 測試工具 看書