Part 1: 在Jest框架中設(shè)計(jì)你的第一則Vue.js單元測試用例

Write the first Vue.js Component Unit Test in Jest

在Jest框架中設(shè)計(jì)你的第一則Vue.js單元測試用例

Learn how to write unit tests with the official VueJS tools and the Jest framework.

學(xué)習(xí)如何運(yùn)用VueJS官方測試工具與Jset框架設(shè)計(jì)單元測試用例

vue-test-utils, the official VueJS testing library and based on avoriaz, is just around the corner. @EddYerburgh is indeed doing a very good job creating it. It provides all necessary tooling for making easy to write unit test in a VueJS application.

首先赤屋,vue-test-utils基于avoriaz跟狱,是VueJS官方的測試庫,集成了眾多測試工具祖能,簡化了對VueJs應(yīng)用進(jìn)行單元測試的設(shè)計(jì)。

Jest, on the other side, is the testing framework developed at Facebook, which makes testing a breeze, with awesome features such as:

另外一方面籽暇,Jest是Facebook開發(fā)的測試框架温治,有很多清爽簡便的特性:

  • Almost no config by default
    • 默認(rèn)情況基本無需用戶配置相關(guān)參數(shù)
  • Very cool interactive mode
    • 交互模式非常酷
  • Run tests in parallel
    • 對用例并發(fā)測試
  • Spies, stubs and mocks out of the box
    • Spies, 可以提供函數(shù)調(diào)用的信息戒悠,但不會改變函數(shù)的行為
    • Stubs, 與spies類似熬荆,但是會完全替換目標(biāo)函數(shù)。這使得一個被stubbed的函數(shù)可以做任何你想要的 —— 例如拋出一個異常绸狐,返回某個特定值等等卤恳。
    • Mocks, 通過組合spies和stubs,使替換一個完整對象更容易寒矿。
  • Built in code coverage
    • 內(nèi)置可視化覆蓋率報(bào)告功能
  • Snapshot testing
    • 快照測試功能
  • Module mocking utilities
    • 實(shí)用的mocking模塊

Probably you’ve already written test without this tools, and just by using karma + mocha + chai + sinon + …, but you’ll see how much easier it can be ??.

也許你曾經(jīng)沒有用過上述工具突琳,只是用karma、mocha符相、chai拆融、sinon等設(shè)計(jì)測試用例,那么接下來你將看到顛覆性的用例設(shè)計(jì)模式啊终。

Set up a vue-test sample project

搭建一個Vue的測試項(xiàng)目

Let’s start by creating a new project using vue-cli answering NO to all yes/no questions:

我們用vue-cli搭設(shè)一個全新的項(xiàng)目冠息,對所有需要您回答“是否需要。孕索。□锾迹”的步驟全部選擇“不要(NO)”搞旭。

    npm install -g vue-cli
    vue init webpack vue-test
    cd vue-test

Then we’ll need to install some dependencies:

然后我們需要安裝以下幾個必要的依賴:

    # Install dependencies
    npm i -D jest jest-vue-preprocessor babel-jest

jest-vue-preprocessor is needed for making jest understand .vue files, and babel-jest for the integration with Babel.

jest-vue-preprocessor是用來識別并解析VUE組件文件的工具,babel-jest則是用來整合Babel工具庫的橋梁菇绵。

As vue-test-utils, it can be installed already from npm, since beta.1 has been published.

vue-test-utils已經(jīng)發(fā)布了beta.1版本肄渗,現(xiàn)在我們已經(jīng)可以在npm上安裝了。

    npm i -D vue-test-utils

Let’s add the following Jest configuration in the package.json:

我們在package.json文件中添加以下Jest的配置:

    ...
    "jest": {
      "moduleNameMapper": {
        "^vue$": "vue/dist/vue.common.js"
      },
      "moduleFileExtensions": [
        "js",
        "vue"
      ],
      "transform": {
        "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
        ".*\\.(vue)$": "<rootDir>/node_modules/jest-vue-preprocessor"
      }
    }
    ...

moduleFileExtensions will tell Jest which extensions to look for, and transform which preprocessor to use for a file extension.

moduleFileExtensions是來告訴Jest掃描哪些類型的文件咬最;transform設(shè)定了各個類型的文件對應(yīng)哪些預(yù)處理工具翎嫡。

At last, add a test script to the package.json:

最后我們添加一段測試腳本在package.jsonscripts里:

    {
      "scripts": {
        "test": "jest",
        ...
      },
      ...
    }

Testing a Component

對一個組件進(jìn)行測試

I’ll be using Single File Components here, and I haven’t checked if it works by splitting them in their own html, css or js files, so let’s assume you’re doing that as well.

我將會用單文件組件進(jìn)行測試,我暫時先不管它的html結(jié)構(gòu)永乌、樣式和js邏輯是否可行惑申,我先假設(shè)你已經(jīng)把他們都處理好了。

First create a MessageList.vue component under src/components:

首先先創(chuàng)建一個MessageList.vue組件翅雏,放在src/components目錄下:

    <template>
        <ul>
            <li v-for="message in messages">
                {{ message }}
            </li>
        </ul>
    </template>

    <script>
    export default {
      name: 'list',
      props: ['messages']
    }
    </script>

And update App.vue to use it, as follows:

然后在App.vue中引入MessageList.vue

    <template>
      <div id="app">
        <MessageList :messages="messages"/>
      </div>
    </template>

    <script>
    import MessageList from './components/MessageList'

    export default {
      name: 'app',
      data: () => ({ messages: ['Hey John', 'Howdy Paco'] }),
      components: {
        MessageList
      }
    }
    </script>

We have already a couple of components that we can test. Let’s create a test folder under the project root, and a App.test.js:

我們已經(jīng)有了一對組件可以進(jìn)行測試圈驼。然后我們在項(xiàng)目根目錄下創(chuàng)建一個測試文件夾和一個App.test.js文件:

    import Vue from 'vue'
    import App from '../src/App'

    describe('App.test.js', () => {
      let cmp, vm

      beforeEach(() => {
        cmp = Vue.extend(App) // Create a copy of the original component
        vm = new cmp({
          data: { // Replace data value with this fake data
            messages: ['XXX']
          }
        }).$mount() // Instances and mounts the component
      })

      it('equals messages to ["XXX"]', () => {
        expect(vm.messages).toEqual(['XXX'])
      })
    })

Right now, if we run npm test (or npm t as a shorthand version), the test should run and pass. Since we’re modifying the tests, let’s better run it in watch mode:

現(xiàn)在我們在命令行執(zhí)行npm test(或短命令npm run t),可以看到用例已經(jīng)被執(zhí)行望几,而且已經(jīng)通過了測試绩脆。如果想觀察修改用例會有什么效果,可以執(zhí)行npm t --watch命令。

    npm t -- --watch

The problem with nested components

測試過程中要注意的嵌套組件問題

This test is too simple. Let’s check that the output is the expected as well. For that we can use the amazing Snapshots feature of Jest, that will generate a snapshot of the output and check it against in the upcoming runs. Add after the previous it in App.test.js:

這個測試有些過于簡單靴迫,我們來檢測一下輸出結(jié)果是否符合我們的預(yù)期惕味。
此時我們可以用Jest神奇的快照功能來進(jìn)行測試,此功能會產(chǎn)生一個輸出結(jié)果的快照玉锌,與接下來后續(xù)測試的結(jié)果進(jìn)行對比名挥。
我們先將一下代碼添加到App.test.js中:

    it('has the expected html structure', () => {
      expect(vm.$el).toMatchSnapshot()
    })

That will create a test/snapshots/App.test.js.snap file. Let’s open it and inspect it:

進(jìn)程會在__snapshots__目錄中產(chǎn)生一個App.test.js.snap文件。
我們打開后看到里面的代碼是:

    // Jest Snapshot v1, https://goo.gl/fbAQLP

    exports[`App.test.js has the expected html structure 1`] = `
    <div
      id="app"
    >
      <ul>
        <li>
          XXX
        </li>
      </ul>
    </div>
    `;

In case you haven’t noticed, there is a big problem here: the MessageList component has been rendered as well. Unit tests must be tested as an independent unit, meaning that in App.test.js we wanna test App component and don’t care at all about anything else.

也許你沒有發(fā)現(xiàn)芬沉,這里有個問題——MessageList組件在父組件的快照中已經(jīng)被成功渲染躺同。

單元測試的理念是單個組件需要被作為獨(dú)立單元檢測,這意味著我們測試App根組件的時候丸逸,其實(shí)是不在乎其中的子組件具體是什么狀態(tài)的蹋艺。

This can be the reason of several problems. Imagine for example, that the children components (MessageList in this case) perform side effect operations on the created hook, such as calling fetch, a Vuex action or state changes? That’s something we definitely don’t want.

這會引起幾個相關(guān)問題。想象一下黄刚,像MessageList這樣的子組件捎谨,會在created鉤子中引發(fā)副作用,比如會不會調(diào)用一些獲取數(shù)據(jù)的方法憔维、Vuex的action被調(diào)用來改變了state中的數(shù)據(jù)涛救?這些不是我們想要看到的。

Luckily, Shallow Rendering solves this nicely.
幸運(yùn)的是业扒,淺渲染可以幫我們完美解決問題检吆!

What is Shallow Rendering?

什么是淺渲染

Shallow Rendering is a technique that assures your component is rendering without children. This is useful for:

淺渲染是確保組件不受子組件影響,被獨(dú)立測試的技術(shù)程储,其用武之地包括:

  • Testing only the component you want to test (that’s what Unit Test stands for)
    • 只測試你想測的組件(這本身就是單元測試的初衷)
  • Avoid side effects that children components can have, such as making HTTP calls, calling store actions…
    • 避免子組件對父組件的影響蹭沛,如請求HTTP、store中的actions等動作

Testing a Component with vue-test-utils

運(yùn)用vue-test-utils來對組件測試

vue-test-utils provide us with Shallow Rendering among other features. We could rewrite the previous test as follows:

vue-test-utils 提供給我們一系列有用的特性章鲤,其中就包括淺渲染摊灭,我們可以改寫一下我們之前寫的測試用例:

    import { shallow } from 'vue-test-utils'
    import App from '../src/App'

    describe('App.test.js', () => {
      let cmp

      beforeEach(() => {
        cmp = shallow(App, { // Create a shallow instance of the component
          data: {
            messages: ['XXX']
          }
        })
      })

      it('equals messages to ["XXX"]', () => {
        // Within cmp.vm, we can access all Vue instance methods
        expect(cmp.vm.messages).toEqual(['XXX'])
      })

      it('has the expected html structure', () => {
        expect(cmp.element).toMatchSnapshot()
      })
    })

And now, if you’re still running Jest in watching mode, you’ll see the test still pass, but the Snapshot doesn’t match. Press u to regenerate it. Open and inspect it again:

現(xiàn)在,如果你之前執(zhí)行的是監(jiān)聽模式败徊,你可以看到測試用例依然通過了帚呼,但是快照已經(jīng)報(bào)錯了,提示不匹配皱蹦。 你可以在命令行用u參數(shù)煤杀,來重新生成快照,我們現(xiàn)在看看快照代碼的內(nèi)容變成什么樣了:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`App.test.js has the expected html structure 1`] = `
<div
  id="app"
>
  <!--  -->
</div>
`;

You see? Now no children have been rendered and we tested the App component fully isolated from the component tree. Also, if you have any created or whatever hooks in the children components, they haven’t been called either ??.

看到了嗎沪哺?現(xiàn)在沒有子組件被渲染后的代碼了怜珍。我們現(xiàn)在終于客觀上完成了對App組件進(jìn)行真正意義上的單元測試。此時無論你在子組件中任何生命周期的鉤子里有什么樣的動作凤粗,都不會被包括在此輪的測試中酥泛。

If you’re curious about how shallow render is implemented, check out the source code and you’ll see that basically is stubbing the components key, the render method and the lifecycle hooks.

如果你好奇淺渲染的底層邏輯是什么今豆,你可以查看到源碼中關(guān)于生命周期鉤子和相關(guān)方法的代碼。

In the same vein, you can implement the MessageList.test.js test as follows:

同樣的方式柔袁,我們可以創(chuàng)建MessageList.test.js文件呆躲,來對MessageList進(jìn)行測試:

    import { shallow } from 'vue-test-utils'
    import MessageList from '../src/components/MessageList'

    describe('MessageList.test.js', () => {
      let cmp

      beforeEach(() => {
        cmp = shallow(MessageList, {
          // Beaware that props is overriden using `propsData`
          propsData: {
            messages: ['XXX']
          }
        })
      })

      it('has received ["XXX"] as the message property', () => {
        expect(cmp.vm.messages).toEqual(['XXX'])
      })

      it('has the expected html structure', () => {
        expect(cmp.element).toMatchSnapshot()
      })
    })
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捶索,隨后出現(xiàn)的幾起案子插掂,更是在濱河造成了極大的恐慌,老刑警劉巖腥例,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辅甥,死亡現(xiàn)場離奇詭異,居然都是意外死亡燎竖,警方通過查閱死者的電腦和手機(jī)璃弄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來构回,“玉大人夏块,你說我怎么就攤上這事∠说В” “怎么了脐供?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長借跪。 經(jīng)常有香客問我政己,道長,這世上最難降的妖魔是什么掏愁? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任歇由,我火速辦了婚禮,結(jié)果婚禮上托猩,老公的妹妹穿的比我還像新娘。我一直安慰自己辽慕,他們只是感情好京腥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溅蛉,像睡著了一般公浪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上船侧,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天欠气,我揣著相機(jī)與錄音,去河邊找鬼镜撩。 笑死预柒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宜鸯,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼憔古,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淋袖?” 一聲冷哼從身側(cè)響起鸿市,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎即碗,沒想到半個月后焰情,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剥懒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年内舟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕊肥。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡谒获,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出壁却,到底是詐尸還是另有隱情批狱,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布展东,位于F島的核電站赔硫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盐肃。R本人自食惡果不足惜爪膊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砸王。 院中可真熱鬧推盛,春花似錦、人聲如沸谦铃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驹闰。三九已至瘪菌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘹朗,已是汗流浹背师妙。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屹培,地道東北人默穴。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓怔檩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親壁顶。 傳聞我的和親對象是個殘疾皇子珠洗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,309評論 0 10
  • 看到別人畫的真人漫畫版,不由也想試試若专,周五和同學(xué)的合照许蓖,拿來練手,一秒變萌的感覺真不錯调衰,哈哈膊爪! 朋友們,喜歡這樣萌...
    木可人兒閱讀 3,844評論 12 10
  • 今天七月一日是市重點(diǎn)中學(xué)考試日嚎莉,女兒要在今天上“戰(zhàn)場”米酬。 女兒已走進(jìn)考場。作為母親趋箩,為她擔(dān)心著赃额,但什么都做不了,只...
    雪漫飛閱讀 305評論 10 5
  • 《天蝎計(jì)劃》第二季第12集“圣誕潰壩” 二〇一六年十二月二日星期五 在圣誕前夜叫确,一座無人值守的大壩出現(xiàn)了潰壩險情跳芳,...
    未來星星閱讀 955評論 0 0
  • 《新科學(xué)家》| 這樣運(yùn)動更聰明 馬徐駿 我們都知道運(yùn)動有助于保持健康,但是在高速轉(zhuǎn)動的生活里竹勉,想要保持運(yùn)動的好習(xí)慣...
    JAMESCAMERONL閱讀 412評論 0 2