<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于 TDD 模式編寫 Vue 評論組件(上):數(shù)據(jù)綁定和列表渲染

          共 11873字,需瀏覽 24分鐘

           ·

          2021-03-17 21:09

          上篇教程學(xué)院君給大家演示了 Vue 組件測試套件(Vue Test Utils + Mocha + jsdom + Expect)的引入和基本使用,今天,我們結(jié)合這套組件通過測試驅(qū)動開發(fā)(Test-Driven Development,簡稱 TDD)模式來編寫一個 Vue 單文件組件。

          前面在組件實戰(zhàn)中我們已經(jīng)基于 Laravel + Vue 組件開發(fā)出了一個簡單的博客應(yīng)用,但是這個博客缺乏與讀者之間的交流和互動,我們可以給它加上評論功能來彌補(bǔ)這個不足:

          下面我們基于 TDD 模式來開發(fā)這個評論組件。

          第一步:創(chuàng)建組件和表單控件

          所謂測試驅(qū)動開發(fā)就是根據(jù)需求先編寫不同場景的測試用例,然后再編寫可以通過這些測試用例的代碼,最終交付出一個質(zhì)量可靠的系統(tǒng)。這個開發(fā)過程通過測試推動,這是一種敏捷開發(fā)模式。

          以本功能為例,我們先在 tests/JavaScript 目錄下新建一個 comment.spec.js 文件用于測試評論組件,然后編寫該組件的第一個測試用例代碼如下:

          import { mount} from "@vue/test-utils";
          import CommentComponent from '../../resources/js/components/CommentComponent.vue';

          describe('CommentComponent.vue', () => {
              it('include comment form with textarea and placeholder'function ({
                  let wrapper = mount(CommentComponent);

                  expect(wrapper.find('form').exists()).toBe(true);
                  expect(wrapper.find('textarea').exists()).toBe(true);
                  expect(wrapper.find('textarea').attributes('placeholder')).toMatch('請輸入評論內(nèi)容...');
              });
          });

          此時運行 npm run test 會測試失敗:

          提示沒有 CommentComponent 組件,我們在 resources/js/components 目錄下創(chuàng)建這個 Vue 組件,并添加一個包含文本框的表單元素:

          <style scoped>

          </style>

          <template>
              <form>
                  <div class="form-group">
                      <textarea class="form-control" name="content" rows="3" placeholder="請輸入評論內(nèi)容..."></textarea>
                  </div>
              </form>

          </template>

          <script>
          export default {}
          </
          script>

          再次運行測試,就可以看到第一個測試用例通過了:

          自動運行測試

          當(dāng)然,每次修改測試用例代碼后都要手動運行 npm run test 很麻煩,我們可以在 package.json 中配置一個 watch-test 命令,每次前端代碼調(diào)整后自動運行 npm run test

          "scripts": {
              ...
              "test""cross-env NODE_ENV=development mochapack --webpack-config webpack.config.js --require tests/JavaScript/setup.js tests/JavaScript/**/*.spec.js",
              "watch-test""npm run test -- --watch"
          },

          在終端執(zhí)行 npm run watch-test 開啟自動運行測試。

          第二步:表單提交按鈕

          我們接下來在 comment.spec.js 中編寫第二個測試用例 —— 為表單添加一個提交按鈕:

          describe('CommentComponent.vue', () => {
              ...

              it('include submit btn'function ({
                  let wrapper = mount(CommentComponent);

                  expect(wrapper.find('button[type=submit]').exists()).toBe(true);
                  expect(wrapper.find('button[type=submit]').text()).toMatch('提交評論');
              });
          });

          現(xiàn)在可以在終端窗口中看到測試已經(jīng)自動運行了,當(dāng)然,現(xiàn)在測試是不同過的:

          因為 CommentComponent 組件還沒有包含提交按鈕,我們編輯這個組件文件添加提交按鈕:

          <template>
              <form>
                  <div class="form-group">
                      <textarea class="form-control" name="content" rows="3" placeholder="請輸入評論內(nèi)容..."></textarea>
                  </div>
                  <div class="text-right">
                      <button type="submit" class="btn btn-primary">提交評論</button>
                  </div>
              </form>
          </template>

          自動測試運行通過:

          這樣我們就已經(jīng)完成了評論功能表單模板代碼的編寫。

          重構(gòu)測試用例

          同一個組件的測試用例代碼通常都在同一個文件中,每次編寫一個新的測試用例都要重復(fù)掛載組件很低效,我們可以像在 PHPUnit 測試類中定義 setUp 方法那樣,在同一個組件的測試用例組中定義一個 beforeEach 方法,該方法會在 CommentComponent 組件每個測試用例運行之前執(zhí)行,我們可以將該組件的掛載工作放到這個方法中實現(xiàn),這樣一來,我們就可以直接在每個測試用例中通過 wrapper 變量來引用掛載實例了:

          describe('CommentComponent.vue', () => {
              let wrapper;

              beforeEach(() => {
                  wrapper = mount(CommentComponent);
              })

              it('include comment form with textarea and placeholder'function ({
                  expect(wrapper.find('form').exists()).toBe(true);
                  expect(wrapper.find('textarea').exists()).toBe(true);
                  expect(wrapper.find('textarea').attributes('placeholder')).toMatch('請輸入評論內(nèi)容...');
              })

              it('include submit button'function ({
                  expect(wrapper.find('button[type=submit]').exists()).toBe(true);
                  expect(wrapper.find('button[type=submit]').text()).toMatch('提交評論');
              })
          });

          代碼瞬間干凈了許多。修改完成會自動運行回歸測試,測試通過,表明此時重構(gòu)沒有引入bug:

          第三步:輸入評論內(nèi)容與數(shù)據(jù)綁定

          現(xiàn)在已經(jīng)具備完整的表單控件了,是時候填寫表單并提交了。在 comment.spec.js 中編寫第三個測試用例 —— 輸入評論內(nèi)容,并將其與 Vue 模型屬性建立數(shù)據(jù)綁定,以便執(zhí)行后續(xù)操作:

          it('typed comment will sync with model data 'function ({
              // Given
              let comment = '大家好,我是學(xué)院君。';
              wrapper.find('textarea[name=content]').element.value = comment;

              // When
              wrapper.find('textarea[name=content]').trigger('input');

              // Then
              expect(wrapper.vm.comment).toBe(comment);
          });

          這個時候,我們就可以編寫純正 BDD 風(fēng)格的測試用例了:

          • Given:初始化資源 —— 輸入評論內(nèi)容;

          • When:觸發(fā) textarea 元素的 input 事件;

          • Then:斷言 Vue 模型屬性 comment 是否和輸入的評論內(nèi)容一致。

          注:相關(guān)的 Wrapper 語法明細(xì)可以參考 Vue 測試套件官方文檔了解。

          這個時候測試用例不通過,因為現(xiàn)在還沒有定義 comment 模型屬性,textarea 表單輸入控件也沒有與之建立對應(yīng)的數(shù)據(jù)綁定:

          打開 CommentComponent 組件,編寫對應(yīng)的實現(xiàn)代碼如下:

          <template>
              <form>
                  <div class="form-group">
                      <textarea v-model="comment" class="form-control" name="content" rows="3" placeholder="請輸入評論內(nèi)容..."></textarea>
                  </div>
                  <div class="text-right">
                      <button type="submit" class="btn btn-primary">提交評論</button>
                  </div>
              </form>

          </template>

          <script>
          export default {
              data() {
                  return {
                      comment: '',
                      comments: []
                  }
              }
          }
          </
          script>

          第三個測試用例運行通過:

          第四步:提交評論并進(jìn)行列表渲染

          編寫好評論內(nèi)容之后還要提交才能保存和渲染,我們在 comment.spec.js 中編寫第四個測試用例來測試這個業(yè)務(wù)場景,這一步是目前最復(fù)雜的:

          it('click submit button will render comment in comments list'function ({
              // Given
              expect(wrapper.find('ul.comments').isVisible()).toBe(false);
              let comment = '大家好,我是學(xué)院君。';
              wrapper.find('textarea[name=content]').element.value = comment;
              wrapper.find('textarea[name=content]').trigger('input');

              // When
              wrapper.find('button[type=submit]').trigger('submit');

              // Then
              expect(wrapper.vm.comments.length).toBe(1);
              expect(wrapper.vm.comments[0]).toBe(comment);
              wrapper.vm.$nextTick(() => {
                  // 需要將這兩個斷言放到 Vue.nextTick 中執(zhí)行,因為它們需要在 DOM 刷新之后才會生效
                  expect(wrapper.find('ul.comments').isVisible()).toBe(true);
                  expect(wrapper.find('ul.comments').html()).toContain(comment);
              })
          });

          我們還是通過 Given-When-Then 三步進(jìn)行拆分:

          • Given:開始的時候評論列表為空,我們通過 textarea 表單元素編寫評論內(nèi)容;

          • When:編寫好了之后點擊提交按鈕,觸發(fā) submit 事件;

          • Then:提交之后會觸發(fā)監(jiān)聽 submit 事件的函數(shù),更新組件模型屬性,將當(dāng)前評論內(nèi)容插入到評論列表,并清空評論框,然后在 Vue.nextTick 回調(diào)中斷言評論列表是否出現(xiàn)并包含剛剛提交的內(nèi)容。

          這里為什么要在 Vue.nextTick(wrapper.vm 對應(yīng)的就是 Vue 實例) 閉包中執(zhí)行斷言代碼呢,馬上會揭曉。

          由于評論組件還沒有實現(xiàn)對應(yīng)的業(yè)務(wù)代碼,所以此時測試用例不通過:

          打開評論組件 CommentComponent.vue,添加評論渲染列表、submit 事件函數(shù)等業(yè)務(wù)代碼如下:

          <style scoped>

          </style>

          <template>
              <div>
                  <form @submit.prevent="addNewComment">
                      <div class="form-group">
                          <textarea v-model="comment" class="form-control" name="content" rows="3" placeholder="請輸入評論內(nèi)容..."></textarea>
                      </div>
                      <div class="text-right">
                          <button type="submit" class="btn btn-primary">提交評論</button>
                      </div>
                  </form>
                  <h3>所有評論</h3>
                  <ul class="comments" v-show="comments.length > 0">
                      <li v-for="(content, index) in comments" :key="index" v-text="content"></li>
                  </ul>
              </div>

          </template>

          <script>
          export default {
              data() {
                  return {
                      comment: '',
                      comments: []
                  }
              },
              methods: {
                  addNewComment() {
                      this.comments.push(this.comment);
                      this.comment = '';
                  }
              }
          }
          </
          script>

          測試用例運行通過,表明業(yè)務(wù)代碼可以正常工作:

          Vue.nextTick 回調(diào)

          這里,我們來簡單說說為什么要在 Vue.nextTick 回調(diào)函數(shù)中執(zhí)行評論列表渲染相關(guān)的斷言,這是因為 Vue 組件中,并不是模型數(shù)據(jù)一變更,頁面 DOM 就跟著更新渲染,底層有自己的更新策略,而 Vue.nextTick 的作用就是在下次 DOM 更新結(jié)束之后執(zhí)行延遲回調(diào),也就是我們上面編寫的斷言代碼,這樣一來,就可以保證在 DOM 更新之后斷言評論列表,否則就會出現(xiàn)列表未更新,測試用例運行不通過的狀況。

          小結(jié)

          至此,我們已經(jīng)初步完成單文件評論組件的前端業(yè)務(wù)功能,接下來,我們需要將其嵌入到文章詳情頁中,并調(diào)用后端接口存儲評論數(shù)據(jù),那么父子組件之前的通信(props、emit)以及前后端接口調(diào)用該如何編寫測試用例呢?學(xué)院君將在下篇教程給大家揭曉。

          本系列教程首發(fā)在Laravel學(xué)院(laravelacademy.org),你可以點擊頁面左下角閱讀原文鏈接查看最新更新的教程。

          瀏覽 34
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久天堂国产精品女人 | 成人aV无码精品国产一区二区 | 五月丁香婷婷激情 | 内射学生妹网站 | 日本黄色网址大全免费 |