顯示具有 mocha 標籤的文章。 顯示所有文章
顯示具有 mocha 標籤的文章。 顯示所有文章

2023年1月14日 星期六

第一個 Vue3 元件測試從 Vitest + Vue Test Utils 開始

延續「mocha + webpack 的 Vue3 元件單元測試」,既然使用 mocha 測試太卡關,這次安裝 Vitest 來試試看!

一、安裝

先安裝 Vitest,另外 Vitest 需要使用 Node >=v14,並安裝 Vite >=v3.0.0,還有 vue3 對應的測試套件

npm install --save-dev vite vitest @vue/test-utils @vitejs/plugin-vue

下面這幾個根據需求來選擇是否安裝

npm install --save-dev jsdom

提供UI介面,可參考 Test UI

npm install --save-dev @vitest/ui


二、設定

先到package.json設定npm指令

"scripts": {
    "test": "npx vitest",
}

在根目錄新增檔案 vitest.config.js

import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'

export default defineConfig({
    plugins: [
        Vue(),
    ],
    test: {
        globals: true,
        environment: 'jsdom',
    },
});

常用的設定像是include指定測試檔案的目錄或名稱等,和exclude排除檔案

export default defineConfig({
    test: {
        include: ['**/*.test.js'],
        exclude: ['**/node_modules/**'],
    },
});

設定路徑的alias

export default defineConfig({
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'src/'),
        },
    },
});

或者可以設定 csssetupFiles來套用css style

export default defineConfig({
    test: {
        css: true,
        setupFiles: './src/vitest/setup.js',
    },
});

src/vitest/setup.js import css

// src/vitest/setup.js

import 'todomvc-app-css/index.css';
import 'todomvc-common/base.css';

其他設定可以參考官方文件

完成基本設定後,就可以開始寫測試囉!



三、測試

針對現有的元件 TodoListEditor 撰寫測試

import { mount } from '@vue/test-utils';
import { test, expect } from 'vitest';
import TodoListEditor from 'components/TodoListEditor.vue';

test('TodoListEditor', () => {
    expect(TodoListEditor).toBeTruthy()

    const wrapper = mount(TodoListEditor, {
        props: {
            todoList,
        },
    });

    expect(wrapper.vm.todoList).toStrictEqual(todoList);
    expect(wrapper.props('todoList')).toStrictEqual(todoList);
});

參考資料



四、執行
npm run test

執行後就可以看到所有的測試項目及結果,若有錯誤也會對應的訊息顯示

有安裝@vitest/ui,可以開啟ui試試看

npm run test -- --ui


五、CI/CD

有了測試之後,我們利用 github action 設定每次 push 分支時,執行 vitest 的測試,請新增檔案 .github/workflows/test.yml

name: Test

on:
  push:
    branches: main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Before srcipt
        run: npm install

      - name: Test
        run: npm run test -- --run

.github/workflows/gh-pages.yml內的設定:

on:
  workflow_run:
    workflows: ["Test"]
    types:
      - completed

jobs:
  build:
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest

    steps:
      ....

我們的目標是當 Test 成功時,才會執行部屬,否則忽略。請注意 completed 指的是完成時,無論測試結果成功與否都會觸發。因此 if: ${{ github.event.workflow_run.conclusion == 'success' }} 的設定是必要的。

也可變化成 ${{ github.event.workflow_run.conclusion == 'failure' }},當測試結果失敗時可以發送通知,根據應用情境改寫。

workflows可以設定多項,只要有任何一項執行就會觸發。


安裝 Vitest 來進行 Vue3 測試十分容易。語法跟 jest 很相像,甚至原本的 mocha 寫的測試也一行都沒改,都能夠支援測試。以上的內容有上傳到 github 上(專案),可以點選下方連結了解執行的內容。


2022年7月24日 星期日

mocha + webpack 的 Vue3 元件單元測試

先說結論,我認為不適合用 mocha 進行 vue3 單元測試(@vue/test-utils),反覆查了很久的資料,相關的套件支援度不足等有重重的障礙,根據 @vue/test-utils 目前提供的測試範例,選擇 Vitest 會更適合。

完整程式碼上傳至 Github (連結)。

一、安裝

首先,第一個問題就是 vue 的版本不能太新,目前只支援 3.0.7,因此對應安裝了相同版本的 @vue/server-renderer

npm install --save-dev @vue/server-renderer@3.0.7

再來請安裝 webpck 和 mocha,mochapack,mochapack 是用來讀取 webpack 設定將元件 render 出來的套件,可支援 webpack5 和 mocha 9

npm install --save-dev webpack mocha mochapack

其他有兩個類似的套件:

mochapack 是由 mocha-webpack 延伸而來的,三者用法都非常接近,但上述兩個對 webpack 和 mocha 版本的支援度都比 mochapack 差


再來請安裝 vue3 官方的元件測試套件 @vue/test-utils

npm install --save-dev @vue/test-utils

mocha 不像是 jest 已經內建支援 jsdom、assertion ,所以要另外安裝

npm install --save-dev webpack-node-externals jsdom-global
npm install --save-dev expect


二、設定

新增測試專用的 webpack 設定檔 webpack.config-test.js

// webpack.config-test.js

const nodeExternals = require('webpack-node-externals');
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
    mode: 'development',
    target: 'node',  // webpack should compile node compatible code
    externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
    devtool: 'inline-cheap-module-source-map',
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
            },
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader'],
            },
        ],
    },
    plugins: [
        new VueLoaderPlugin(),
    ],
};

設定 jsdom 設定檔 src/tests/setup.js

// src/tests/setup.js

require('jsdom-global')();

最後,請到 package.json 設定 script 指令

Usage: mochapack [options] [<file|directory|glob> ...]

Options
 --webpack-config  path to webpack-config file
 --require, -r     require the given module
{
  "scripts": {
    "test": "npx mochapack --webpack-config webpack.config-test.js --require src/tests/setup.js src/tests/**/*.spec.js",
  }
}


三、執行
新增測試(範例請參考 Counter.spec.js)後,執行指令即可
npm run test


四、限制

1. 無法支援目前 vue 最新版本 3.2.37,發現底下錯誤訊息:

ReferenceError: SVGElement is not defined

2. 無法支援 SFC ,發現底下警告訊息:

[Vue warn]: Component is missing template or render function.


參考文件:


2022年2月22日 星期二

Cypress+Vue 元件測試 (component testing)

接續「e2e 測試工具 Cypress 安裝及介紹」,除了用在實際開啟瀏覽器的測試,cypress也支援元件測試,需要安裝相關的套件將元件render出來。


安裝
npm install --save-dev cypress @cypress/vue @cypress/webpack-dev-server webpack-dev-server

如果是 Vue3 ,請記得把 @cypress/vue 換成:

npm install --save-dev @cypress/vue@next


設定

先到 cypress/plugins/index.js 的註冊 dev-server:start 事件,再來要注意 require('@vue/cli-service/webpack.config.js')這段,你的專案不見得有安裝這個套件,你可以建立自己的webpack設定,並替換上正確的路徑。

// cypress/plugins/index.js

const { startDevServer } = require('@cypress/webpack-dev-server');
// Vue's Webpack configuration
const webpackConfig = require('@vue/cli-service/webpack.config.js');

module.exports = (on, config) => {
  on('dev-server:start', options =>
    startDevServer({ options, webpackConfig });
  );
};

若我們要測試的元件內容為:

<!-- Button.vue -->
<template>
  <button>
    <slot />
  </button>
</template>

在測試時,vue2和vue3引入 mount 的寫法相同,並且將設定元件所需的輸入

import {mount} from '@cypress/vue'
import Button from './Button.vue';

it('Button', () => {
  mount(Button, {
    slots: {
      default: 'Test button',
    },
  });

  cy.get('button').contains('Test button').click();
});


執行

提供 GUI 介面操作

npx cypress open-ct

無GUI,在 Terminal 上執行

npx cypress run-ct


參考文件: https://docs.cypress.io/guides/component-testing/introduction


2022年1月31日 星期一

e2e 測試工具 Cypress 安裝及介紹


e2e測試(End-to-End testing)是模擬使用者的行為,實際打開瀏覽器操作頁面的測試方式,因此不限於vue, react的專案,e2e的測試可以反映出頁面在不同瀏覽器的執行情形。

Cypress便是其中一個常見工具,Cypress在安裝上簡單,語法採用 mocha 的句法、chai 的斷詞(expect, should, assert),撰寫的方式跟 unit test 相似。


安裝

進到想要測試的專案目錄內,使用 npm 安裝。(官網說明)

npm install cypress --save-dev

接著在目錄裡,建立先建立一個 cypress.json,內容放一個空物件,此時你可以直接執行下面的命令(官網說明):

npx cypress open

會發現多了一個 cypress/ 的資料夾,裡面有四個目錄:

  • integration: 主要的測試檔案
  • fixtures: 固定的資料
  • plugins: 主要有 on 定義全局的不同時間點的執行工作,以及 config 提供全局使用的參數,如常用的 env
  • support: 可提供自定義的指令、或引入擴充的指令,供撰寫測試檔案使用


Cypress.json 與環境變數(.env)

使用 cypress 都必須在專案的根目錄裡新增 cypress.json,如果直接放 {} 空物件,代表直接使用預設值。

下面是我的設定,從字面上不難理解設定的內容,第一個testFiles是測試的檔案名稱,接著各個目錄的路徑,video是 Boolean 決定是否錄製測試過程的影片,screenshotOnRunFailure也是 Boolean 是否在執行失敗時擷取畫面。

{
    "testFiles": "**/*.spec.js",
    "integrationFolder": "resources/cypress/integration",
    "componentFolder": "resources/cypress/components",
    "fixturesFolder": "resources/cypress/fixtures",
    "pluginsFile": "resources/cypress/plugins",
    "supportFile": "resources/cypress/support",
    "video": false,
    "screenshotOnRunFailure": false
}

除了cypress基本的設定,常常也需要讀取環境設定檔(.env),假設內容如下(官方說明):

APP_URL=https://chenuin.github.io/
USER_NAME=chenuin

plugins/index.js可以設定:

// plugins/index.js

require('dotenv').config();

module.exports = (on, config) => {
    config.baseUrl = process.env.APP_URL;

    config.env.USER_NAME = process.env.USER_NAME;

    return config;
}
測試檔案需要使用時,可以直接呼叫:
Cypress.config('baseUrl')
Cypress.env('USER_NAME')

打開GUI,點選上方的 Settings 可以看到設定的結果,上面的顏色可以區分目前的設定是讀取哪裡,像是 baseUrl 是紫色的 plugin,

因此,可以知道下面兩種寫法都可以達到相同的效果:

// plugins/index.js
config.baseUrl = process.env.APP_URL;


// cypress.json
{
    "baseUrl": "https://chenuin.github.io/"
}


執行

提供 GUI 介面操作

npx cypress open

無GUI,在 Terminal 上執行

npx cypress run


訪問頁面 visit

測試的第一步常常是先進入某一個頁面,通常專案內都在相同的Domain下,如果有設定 baseUrl,Cypress會直接將設定作為前綴,導到相應的頁面,在寫法上簡潔許多。

// https://chenuin.github.io/webpack/2021/08/08/webpack5-getting-started.html

cy.visit('/webpack/2021/08/08/webpack5-getting-started.html')

若未設定,需要傳入完整的路徑,或者cypress會嘗試在你的web server上找到對應位置的頁面。(官方說明)


intercept與fixtures的使用

使用 intercept 來監聽由頁面上請求(request)及回應(response),下面三者是相同的意思:

cy.intercept('/users/**')
cy.intercept('GET', '/users/**')
cy.intercept({
  method: 'GET',
  url: '/users/**',
})

你也可以為這個路由(route)新增別名(alias),除了方便辨識外,如果需要在request後執行的工作,可以使用下面的寫法:

cy.intercept('POST', '/users').as('createUser');

// once a request to create user responds, 'cy.wait' will resolve
cy.wait('@createUser')

GUI上可以看到這個 route 表格

執行此請求時會留下Log記錄,所有被監聽的路由會列在route表格,表格上可以看到所有route被呼叫的次數(表格最右側的數字),對應的別名(alias)是什麼。

上面 Stubbed 是代表回應(response)是否被取代了,這個是很好用的功能,例如:當我們將 users 這個請求的回應,取代為一個列表:

// requests to '/users' will be fulfilled
// with a body of list: ['John', 'Ben', 'Mary]

cy.intercept(
  '/users',
  ['John', 'Ben', 'Mary],
)

或者可以使用 fixture ,後面填寫 fixture/ 目錄裡的檔案名稱。

// requests to '/users' will be fulfilled
// with the contents of the "users.json" fixture

cy.intercept(
  '/users',
  { fixture: 'users.json' },
)
(官方說明)


ESLint設定

請在 .eslintrc.js 加上設定,不需要安裝任何套件:

module.exports = {
  extends: [
    'plugin:cypress/recommended',
  ],
};



2022年1月25日 星期二

js 單元測試套件 Macha+Chai (es6 語法 Babel 設定)

安裝

首先安裝 mocha (官網)和 chai (官網)

npm install mocha chai --save-dev

chai 是一種斷言庫(Assertion Library),mocha 不像是jest已經內建,可以自行決定要使用哪一種套件,chai是其中最常見的一種。

package.json加入測試指令

"scripts": {
  "test": "npx mocha"
}


設定 Babel

安裝 Babel,再來的說明都會直接使用es6的語法,如果不需要,可以略過這部分。

npm install @babel/core @babel/preset-env @babel/register --save-dev

接著在根目錄新增 babel.config.js

// babel.config.js

module.exports = {
    presets: [
        '@babel/preset-env',
    ],
};

接著在根目錄新增 .mocharc.jsspec是設定 mocha 執行測試的檔案,預設為 test/ ,這裡設定成自動讀取 src/test/ 底下所有以 spec.js 為結尾的檔案。

require可以接陣列設定所需的 module

// .mocharc.js

module.exports = {
    spec: ['src/test/*.spec.js'],
    require: ['@babel/register'],
};

.mocharc.js 是 mocha 執行時會自動讀取的設定檔,也支援json 或 yaml(說明),可以選擇喜歡的格式新增,設定檔的內容可以參考:https://github.com/mochajs/mocha/tree/master/example/config

設定參考資料:
https://github.com/mochajs/mocha-examples/tree/master/packages/babel



建立測試

新增一個簡單的測試

// src/test/array.spec.js

import chai from 'chai';

describe('Array', () => {
  describe('#indexOf()', () => {
    it('should return -1 when the value is not present', function() {
      chai.assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});

馬上執行試試看

npm run test

應該會看到下面的資訊,

> vue3-webpack5-template@1.0.0 test
> npx mocha


  Array
      #indexOf()
          should return -1 when the value is not present

1 passing (8ms)

完成第一個測試囉!



支援 async/await

請安裝套件

npm install @babel/plugin-transform-runtime --save-dev

把套件加到 babel.config.js的 plugin 設定

module.exports = {
    require: ['@babel/register'],
    plugins: ['@babel/plugin-transform-runtime'],
};


Root Hooks

mocha 提供的 hook 包含 before(), after(), beforeEach(), and afterEach(),從語意應該不難看出用途,前兩個只會執行一次,後兩個則是每項測試都會執行。

若要在所有的測試上加上 Hook,則是有這四種 beforeAll(), afterAll(), beforeEach(), and afterEach(),執行時機不變,請根據情境加上執行內容:

// src/test/hooks.js

export const mochaHooks = {
  beforeEach: () => {
    // do something before every test

  }
};

接著在 .mocharc.js 加上設定

module.exports = {
    spec: ['src/test/*.spec.js'],
    require: [
      '@babel/register',
      'src/test/hooks.js',
    ],
};

https://mochajs.org/#hooks
https://mochajs.org/#defining-a-root-hook-plugin



測試結果

mocha 是個可愛的名稱,測試結果也可以換成很多有趣的模式:

npx mocha --report landing

https://mochajs.org/#reporters



支援瀏覽器介面

加上指定的路徑執行下面的指令

npx mocha init [PATH]
  • index.html
  • mocha.css
  • mocha.js
  • test.spec.js

目錄底下會多了上述這些檔案,此時只要將 test.spec.js 加入你的測試,再重新整理 index.html,就能看到測試結果已經輸出在畫面上。


可以將 chai 加入 index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Mocha</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="mocha.css" />
    <script src="https://unpkg.com/chai/chai.js"></script>
  </head>
  <body>
    <div id="mocha"></div>
    <script src="mocha.js"></script>
    <script>
      mocha.setup('bdd');
    </script>
    <script src="tests.spec.js"></script>
    <script>
      mocha.run();
    </script>
  </body>
</html>

再來可以直接將前面新增的 src/test/array.spec.js 內容貼過去,index.html 已經載入 mocha 和 chai ,記得把第一行的 import 拿掉。