亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

在Vue項目中使用snapshot測試的具體使用

2019-11-19 11:46:07
字體:
來源:轉載
供稿:網友

snapshot介紹

snapshot測試又稱快照測試,可以直觀地反映出組件UI是否發生了未預見到的變化。snapshot如字面上所示,直觀描述出組件的樣子。通過對比前后的快照,可以很快找出UI的變化之處。

第一次運行快照測試時會生成一個快照文件。之后每次執行測試的時候,會生成一個快照,然后對比最初生成的快照文件,如果沒有發生改變,則通過測試。否則測試不通過,同時會輸出結果,對比不匹配的地方。

jest中的快照文件以為snap拓展名結尾,格式如下(ps: 在沒有了解之前,我還以為是快照文件是截圖)。一個快照文件中可以包含多個快照,快照的格式其實是HTML字符串,對于UI組件,其HTML會反映出其內部的state。每次測試只需要對比字符串是否符合初始快照即可。

exports[`button 1`] = `"<div><span class=//"count//">1</span> <button>Increment</button> <button class=//"desc//">Descrement</button> <button class=//"custom//">not emitted</button></div>"`;

snapshot測試不通過的原因有兩個。一個原因是組件發生了未曾預見的變化,此時應檢查代碼。另一個原因是組件更新而快照文件并沒有更新,此時要運行jest -u更新快照。

› 1 snapshot failed from 1 test suite. Inspect your code changes or re-run jest with -u to update them.

結合Vue進行snapshot測試

生成快照時需要渲染并掛載組件,在Vue中可以使用官方的單元測試實用工具Vue Test Utils

Vue Test Utils 提供了mount、shallowMount這兩個方法,用于創建一個包含被掛載和渲染的 Vue 組件的 Wrapper。component是一個vue組件,options是實例化Vue時的配置,包括掛載選項和其他選項(非掛載選項,會將它們通過extend覆寫到其組件選項),結果返回一個包括了一個掛載組件或 vnode,以及測試該組件或 vnode 的方法的Wrapper實例。

mount(component:{Component}, options:{Object})

shallowMount與mount不同的是被存根的子組件,詳細請戳文檔。

Wrapper上的豐富的屬性和方法,足以應付本文中的測試需求。html()方法返回Wrapper DOM 節點的 HTML 字符串。find()和findAll()可以查找Wrapper里的DOM節點或Vue組件,可用于查找監聽事件的元素。trigger可以在DOM節點/組件上觸發一個事件。

結合上述的方法,我們可以完成一個模擬事件觸發的快照測試。

細心的讀者可能會發現,我們平時在使用Vue時,數據更新后視圖并不會立即更新,需要在nextTick回調中處理更新完成后的任務。但在 Vue Test Utils 中,為簡化用法,更新是同步的,所以無需在測試中使用 Vue.nextTick 來等待 DOM 更新。

demo演示

Vue Test Utils官方文檔中提供了一個集成VTU和Jest的demo,不過這個demo比較舊,官方推薦用CLI3創建項目。

執行vue create vue-snapshot-demo創建demo項目,創建時要選擇單元測試,提供的庫有Mocha + Chai及Jest,在這里選擇Jest.安裝完成之后運行npm run serve即可運行項目。

本文中將用一個簡單的Todo應用項目來演示。這個Todo應用有簡單的添加、刪除和修改Todo項狀態的功能;Todo項的狀態有已完成和未完成,已完成時不可刪除,未完成時可刪除;已完成的Todo項會用一條線橫貫文本,未完成項會在鼠標懸浮時展示刪除按鈕。

組件簡單地劃分為Todo和TodoItem。TodoItem在Todo項未完成且觸發mouseover事件時會展示刪除按鈕,觸發mouseleave時則隱藏按鈕(這樣可以在快照測試中模擬事件)。TodoItem中有一個checkbox,用于切換Todo項的狀態。Todo項完成時會有一個todo-finished類,用于實現刪除線效果。

為方便這里只介紹TodoItem組件的代碼和測試。

<template> <li  :class="['todo-item', item.finished?'todo-finished':'']"  @mouseover="handleItemMouseIn"  @mouseleave="handleItemMouseLeave" >  <input type="checkbox" v-model="item.finished">  <span class="content">{{item.content}}</span>  <button class="del-btn" v-show="!item.finished&&hover" @click="emitDelete">delete</button> </li></template><script>export default { name: "TodoItem", props: {  item: Object }, data() {  return {   hover: false  }; }, methods: {  handleItemMouseIn() {   this.hover = true;  },  handleItemMouseLeave() {   this.hover = false;  },  emitDelete() {   this.$emit("delete");  } }};</script><style lang="scss">.todo-item { list-style: none; padding: 4px 16px; height: 22px; line-height: 22px; .content {  margin-left: 16px; } .del-btn {  margin-left: 16px; } &.todo-finished {  text-decoration: line-through; }}</style>

進行快照測試時,除了測試數據渲染是否正確外還可以模擬事件。這里只貼快照測試用例的代碼,完整的代碼戳我。

describe('TodoItem snapshot test', () => {  it('first render', () => {    const wrapper = shallowMount(TodoItem, {      propsData: {        item: {          finished: true,          content: 'test TodoItem'        }      }    })    expect(wrapper.html()).toMatchSnapshot()  })  it('toggle checked', () => {    const renderer = createRenderer();    const wrapper = shallowMount(TodoItem, {      propsData: {        item: {          finished: true,          content: 'test TodoItem'        }      }    })    const checkbox = wrapper.find('input');    checkbox.trigger('click');    renderer.renderToString(wrapper.vm, (err, str) => {      expect(str).toMatchSnapshot()    })  })    it('mouseover', () => {    const renderer = createRenderer();    const wrapper = shallowMount(TodoItem, {      propsData: {        item: {          finished: false,          content: 'test TodoItem'        }      }    })    wrapper.trigger('mouseover');    renderer.renderToString(wrapper.vm, (err, str) => {      expect(str).toMatchSnapshot()    })  })})

這里有三個測試。第二個測試模擬checkbox點擊,將Todo項從已完成切換到未完成,期待類todo-finished會被移除。第三個測試在未完成Todo項上模擬鼠標懸浮,觸發mouseover事件,期待刪除按鈕會展示。

這里使用toMatchSnapshot()來進行匹配快照。這里生成快照文件所需的HTML字符串有wrapper.html()和Renderer.renderToString這兩種方式,區別在于前者是同步獲取,后者是異步獲取。

測試模擬事件時,最好以異步方式獲取HTML字符串。同步方式獲取的字符串并不一定是UI更新后的視圖。

盡管VTU文檔中說所有的更新都是同步,但實際上在第二個快照測試中,如果使用expect(wrapper.html()).toMatchSnapshot(),生成的快照文件中Todo項仍有類todo-finished,期待的結果應該是沒有類todo-finished,結果并非更新后的視圖。而在第三個快照測試中,使用expect(wrapper.html()).toMatchSnapshot()生成的快照,按鈕如期望展示,是UI更新后的視圖。所以才不建議在DOM更新的情況下使用wrapper.html()獲取HTML字符串。

下面是兩種對比的結果,1是使用wrapper.html()生成的快照,2是使用Renderer.renderToString生成的。

exports[`TodoItem snapshot test mouseover 1`] = `<li class="todo-item"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn" style="">delete</button></li>`;exports[`TodoItem snapshot test mouseover 2`] = `<li class="todo-item"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn">delete</button></li>`;exports[`TodoItem snapshot test toggle checked 1`] = `<li class="todo-item todo-finished"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn" style="display: none;">delete</button></li>`;exports[`TodoItem snapshot test toggle checked 2`] = `<li class="todo-item"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn" style="display:none;">delete</button></li>`;

這里使用vue-server-renderer提供的createRenderer來生成一個Renderer實例,實例方法renderToString來獲取HTML字符串。這種是典型的回調風格,斷言語句在回調中執行即可。

  // ...  wrapper.trigger('mouseover');  renderer.renderToString(wrapper.vm, (err, str) => {    expect(str).toMatchSnapshot()  })

如果不想使用這個庫,也可以使用VTU中提供的異步案例。由于wrapper.html()是同步獲取,所以獲取操作及斷言語句需要在Vue.nextTick()返回的Promise中執行。

  // ...  wrapper.trigger('mouseover');  Vue.nextTick().then(()=>{    expect(wrapper.html()).toMatchSnapshot()  })

觀察測試結果

執行npm run test:unit或yarn test:unit運行測試。

初次執行,終端輸出會有Snapshots: 3 written, 3 total這一行,表示新增三個快照測試,并生成初始快照文件。

 › 3 snapshots written.Snapshot Summary › 3 snapshots written from 1 test suite.Test Suites: 1 passed, 1 totalTests:    7 passed, 7 totalSnapshots:  3 written, 3 totalTime:    2.012sRan all test suites.Done in 3.13s.

快照文件如下示:

// Jest Snapshot v1, https://goo.gl/fbAQLPexports[`TodoItem snapshot test first render 1`] = `<li class="todo-item todo-finished"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn" style="display: none;">delete</button></li>`;exports[`TodoItem snapshot test mouseover 1`] = `<li class="todo-item"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn">delete</button></li>`;exports[`TodoItem snapshot test toggle checked 1`] = `<li class="todo-item"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn" style="display:none;">delete</button></li>`;

第二次執行測試后,輸出中有Snapshots: 3 passed, 3 total,表示有三個快照測試成功通過,總共有三個快照測試。

Test Suites: 1 passed, 1 totalTests:    7 passed, 7 totalSnapshots:  3 passed, 3 totalTime:    2sRan all test suites.Done in 3.11s.

修改第一個快照中傳入的content,重新運行測試時,終端會輸出不匹配的地方,輸出數據的格式與Git類似,會標明哪一行是新增的,哪一行是被刪除的,并提示不匹配代碼所在行。

  - Snapshot  + Received  - <li class="todo-item todo-finished"><input type="checkbox"> <span class="content">test TodoItem</span> <button class="del-btn" style="display: none;">delete</button></li>  + <li class="todo-item todo-finished"><input type="checkbox"> <span class="content">test TodoItem content change</span> <button class="del-btn" style="display: none;">delete</button></li>   88 |       }   89 |     })  > 90 |     expect(wrapper.html()).toMatchSnapshot()     |                ^   91 |   })   92 |   93 |   it('toggle checked', () => {   at Object.toMatchSnapshot (tests/unit/TodoItem.spec.js:90:32)

同時會提醒你檢查代碼是否錯誤或重新運行測試并提供參數-u以更新快照文件。

Snapshot Summary › 1 snapshot failed from 1 test suite. Inspect your code changes or re-run jest with `-u` to update them.

執行npm run test:unit -- -u或yarn test:unit -u更新快照,輸出如下示,可以發現有一個快照測試的輸出更新了。下次快照測試對照的文件是這個更新后的文件。

Test Suites: 1 passed, 1 totalTests:    7 passed, 7 totalSnapshots:  1 updated, 2 passed, 3 totalTime:    2.104s, estimated 3sRan all test suites.Done in 2.93s.

其他

除了使用toMatchSnapshot()外,還可以使用toMatchInlineSnapshot()。二者不同之處在于toMatchSnapshot()從快照文件中查找快照,而toMatchInlineSnapshot()則將傳入的參數當成快照文件進行匹配。

配置Jest

Jest配置可以保存在jest.config.js文件里,可以保存在package.json里,用鍵名jest表示,同時也允許行內配置。

介紹幾個常用的配置。

rootDir

查找Jest配置的目錄,默認是pwd。

testMatch

jest查找測試文件的匹配規則,默認是[ "**/__tests__/**/*.js?(x)", "**/?(*.)+(spec|test).js?(x)" ]。默認查找在__test__文件夾中的js/jsx文件和以.test/.spec結尾的js/jsx文件,同時包括test.js和spec.js。

snapshotSerializers

生成的快照文件中HTML文本沒有換行,是否能進行換行美化呢?答案是肯定的。

可以在配置中添加snapshotSerializers,接受一個數組,可以對匹配的快照文件做處理。jest-serializer-vue這個庫做的就是這樣任務。

如果你想要實現這個自己的序列化任務,需要實現的方法有test和print。test用于篩選處理的快照,print返回處理后的結果。

后記

在未了解測試之前,我一直以為測試是枯燥無聊的。了解過快照測試后,我發現測試其實蠻有趣且實用,同時由衷地感嘆快照測試的巧妙之處。如果這個簡單的案例能讓你了解快照測試的作用及使用方法,就是我最大的收獲。

如果有問題或錯誤之處,歡迎指出交流。

參考鏈接

vue-test-utils-jest-example
Jest - Snapshot Testing
Vue Test Utils
Vue SSR 指南

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美精品videossex性护士| 国产精品精品一区二区三区午夜版| 日本一区二区三区四区视频| 国产精品亚洲视频在线观看| 欧美亚洲视频在线看网址| 亚洲经典中文字幕| 久久69精品久久久久久国产越南| 亚洲精品久久久久中文字幕二区| 国产精品久久久久aaaa九色| 欧美视频一区二区三区…| 在线看国产精品| 中文日韩在线视频| 91久久精品国产91久久性色| 波霸ol色综合久久| 中文字幕在线看视频国产欧美| 欧洲精品毛片网站| 国产91在线高潮白浆在线观看| 久久久视频精品| 精品国偷自产在线视频| 欧美成人免费网| 日韩视频―中文字幕| 91香蕉嫩草影院入口| 91国在线精品国内播放| 97香蕉超级碰碰久久免费软件| 久久精品青青大伊人av| 久久久av免费| 国产精品va在线播放我和闺蜜| 伊人精品在线观看| 中日韩午夜理伦电影免费| 国产一区在线播放| 亚洲欧美另类中文字幕| 91社区国产高清| 日韩久久免费电影| 91免费看片在线| 亚洲人成电影网站色…| 亚洲成av人乱码色午夜| 国产精品欧美日韩一区二区| 日韩av大片免费看| 成人免费视频网址| 日韩精品视频免费专区在线播放| 亚洲最大福利视频| 国产精品视频xxxx| 中文字幕日韩欧美在线视频| 欧美黑人xxxⅹ高潮交| 日韩欧美中文免费| 精品国产乱码久久久久久虫虫漫画| 国产精品久久二区| 欧美视频在线观看免费网址| 精品无码久久久久久国产| 2019国产精品自在线拍国产不卡| 久久6免费高清热精品| 国产精品久久久久一区二区| 国产一区二区色| 久久久天堂国产精品女人| 欧洲午夜精品久久久| 福利视频第一区| 久久99精品国产99久久6尤物| 亚洲免费视频网站| 日韩视频在线一区| 亚洲最大在线视频| 久久69精品久久久久久国产越南| 91精品国产九九九久久久亚洲| 日韩精品视频在线播放| 国模视频一区二区三区| 成人黄色短视频在线观看| 精品久久久久久久久久久久久久| 欧美日韩国产色视频| 狠狠躁夜夜躁人人爽天天天天97| 亚洲成人久久电影| 综合激情国产一区| 国产精品自拍小视频| 国产成人精品视频| 国产精品xxxxx| 国产精品三级久久久久久电影| 91成人天堂久久成人| 久久久久久久久久国产| 97视频在线看| 亚洲xxx自由成熟| 欧美在线视频在线播放完整版免费观看| 国产精品草莓在线免费观看| 欧美激情久久久久| 国产日韩一区在线| 久久影院在线观看| 久久精品一本久久99精品| 亚洲国产毛片完整版| 九九久久久久99精品| 18性欧美xxxⅹ性满足| 亚洲精品自在久久| 国产精品网址在线| 欧美精品久久久久| 久久69精品久久久久久久电影好| 亚洲男人的天堂在线| 一级做a爰片久久毛片美女图片| 亚洲美女精品久久| 色偷偷av一区二区三区乱| 国产精品视频久久久久| 日韩精品极品毛片系列视频| 国产欧美精品一区二区三区介绍| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美高清不卡在线| 精品国产区一区二区三区在线观看| 欧美激情一区二区久久久| 国产精品美女久久久久久免费| 欧美www视频在线观看| 国产亚洲激情视频在线| 亚洲国产古装精品网站| 2019中文字幕在线免费观看| 九九热精品在线| 亚洲91精品在线| 欧美福利视频在线| 久久九九免费视频| 国产一区二区三区在线播放免费观看| 992tv成人免费视频| 欧美亚洲一区在线| 欧美大片免费观看在线观看网站推荐| 欧美电影免费观看大全| 国产视频久久久| 欧美极品少妇xxxxⅹ免费视频| 亚洲欧美一区二区三区情侣bbw| 一区三区二区视频| 国产精品99久久久久久久久久久久| 亚洲第一天堂无码专区| 国产一区二区日韩精品欧美精品| 午夜精品一区二区三区视频免费看| 奇米一区二区三区四区久久| 久久久久久97| 国产精品v片在线观看不卡| 韩国国内大量揄拍精品视频| 欧美成人精品一区| 日韩网站在线观看| 亚洲护士老师的毛茸茸最新章节| 国产91久久婷婷一区二区| 黑人精品xxx一区| 国产一区二区三区精品久久久| 精品国产91久久久久久| 欧美成人小视频| 成人免费视频网址| 九九精品在线视频| 亚洲网在线观看| 亚洲黄色www| 日本在线观看天堂男亚洲| 亚洲精品丝袜日韩| 欧美视频一区二区三区…| 国产www精品| 成人av色在线观看| 在线国产精品视频| 国产日韩欧美在线视频观看| 中文字幕国内精品| 91精品国产高清久久久久久| 久久亚洲影音av资源网| 日本视频久久久| 国产精品69av| 国产xxx69麻豆国语对白| 奇米4444一区二区三区| 国产aaa精品| 亚洲国产精品热久久| 久热精品视频在线免费观看| 最近2019中文字幕一页二页| 欧美日韩中文字幕| 亚洲第一天堂av| 在线观看日韩欧美| 欧美午夜激情视频| 成人信息集中地欧美| 国产精品成人久久久久|