簡體   English   中英

如何為使用Vuex存儲的Vue表單組件編寫Jest單元測試?

[英]How to write Jest unit test for a Vue form component which uses a Vuex store?

我有一個登錄表單。 當我用數據填寫登錄表單並單擊登錄按鈕時:

  • 表單數據(用戶名,密碼)將發送到服務器並返回響應
  • 如果表單數據無效,則<flash-message>組件將顯示一條消息
  • 如果表單數據有效,則會將用戶重定向到儀表板

由於此組件在很大程度上取決於Vuex存儲,因此我無法考慮此組件的一些有效測試用例。

  • 這個組件可以測試嗎?
  • 如果它可測試的,我如何在開玩笑中編寫單元測試?
  • 我應該模擬我組件的哪個部分?
  • 我應該使用vue-test-utils mount / shallowMount方法來包裝我的組件嗎?
  • 我的組件使用Bootstrap-Vue UI組件。 我該如何處理它們?

我沒有JavaScript生態系統的經驗,所以請大家詳細解釋。

Login.vue

<template>
  <b-col sm="6" offset-sm="3">
    <h1><span class="fa fa-sign-in"></span> Login</h1>
    <flash-message></flash-message>
    <!-- LOGIN FORM -->
    <div class="form">
        <b-form-group>
            <label>Email</label>
            <input type="text" class="form-control" name="email" v-model="email">
        </b-form-group>

        <b-form-group>
            <label>Password</label>
            <input type="password" class="form-control" name="password" v-model="password">
        </b-form-group>

        <b-btn type="submit" variant="warning" size="lg" @click="login">Login</b-btn>
    </div>

    <hr>

    <p>Need an account? <b-link :to="{name:'signup'}">Signup</b-link></p>
    <p>Or go <b-link :to="{name:'home'}">home</b-link>.</p>
  </b-col>

</template>

<script>
export default {
  data () {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    async login () {
      this.$store.dispatch('login', {data: {email: this.email, password: this.password}, $router: this.$router})
    }
  }
}
</script>

Vue test utils文檔說:

[W]建議編寫斷言組件公共接口的測試,並將其內部視為黑盒子。 單個測試用例會斷言提供給組件的某些輸入(用戶交互或道具更改)會產生預期的輸出(渲染結果或發出的自定義事件)。

所以我們不應該測試bootstrap-vue組件,這是該項目維護者的工作。

編寫代碼時要考慮單元測試

為了更容易測試組件,確定它們的全部責任范圍將有所幫助。 這意味着登錄表單應該是它自己的SFC(單個文件組件),登錄頁面是另一個使用登錄表單的SFC。

在這里,我們從登錄頁面隔離了登錄表單。

<template>
    <div class="form">
        <b-form-group>
            <label>Email</label>
            <input type="text" class="form-control" 
                   name="email" v-model="email">
        </b-form-group>

        <b-form-group>
            <label>Password</label>
            <input type="password" class="form-control" 
                   name="password" v-model="password">
        </b-form-group>

        <b-btn type="submit" variant="warning" 
               size="lg" @click="login">
               Login
        </b-btn>
    </div>
</template>

<script>
export default {
    data() {
        return { email: '', password: '' };
    },
    methods: {
        login() {
            this.$store.dispatch('login', {
                email: this.email,
                password: this.password
            }).then(() => { /* success */ }, () => { /* failure */ });
        }
    }
}
</script>

我從商店操作調度中刪除了路由器,因為當登錄成功或失敗時,處理重定向不是商店的責任。 商店不應該知道它前面有一個前端。 它處理與數據相關的數據和異步請求。

獨立測試每個部分

單獨測試商店操作。 然后他們可以在組件中完全嘲笑。

測試商店操作

在這里,我們希望確保商店能夠完成它的目的。 因此,我們可以檢查狀態是否具有正確的數據,在模擬它們時進行HTTP調用。

import Vuex from 'vuex';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import storeConfig from '@/store/config';

describe('actions', () => {
    let http;
    let store;

    beforeAll(() => {
        http = new MockAdapter(axios);
        store = new Vuex.Store(storeConfig());
    });

    afterEach(() => {
        http.reset();
    });

    afterAll(() => {
        http.restore();
    });

    it('calls login and sets the flash messages', () => {
        const fakeData = { /* ... */ };
        http.onPost('api/login').reply(200, { data: fakeData });
        return store.dispatch('login')
            .then(() => expect(store.state.messages).toHaveLength(1));
    });
    // etc.
});

測試我們的簡單LoginForm

這個組件唯一真正的做法是在調用提交按鈕時調度login操作。 所以我們應該測試一下。 我們不需要測試動作本身,因為它已經單獨測試過。

import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import LoginForm from '@/components/LoginForm';

const localVue = createLocalVue();
localVue.use(Vuex);

describe('Login form', () => {

    it('calls the login action correctly', () => {
        const loginMock = jest.fn(() => Promise.resolve());
        const store = new Vuex.Store({
            actions: {
                // mock function
                login: loginMock
            }
        });
        const wrapper = mount(LoginForm, { localVue, store });
        wrapper.find('button').trigger('click');
        expect(loginMock).toHaveBeenCalled();
    });
});

測試Flash消息組件

同樣,我們應該使用注入的消息模擬存儲狀態,並確保FlashMessage組件通過測試每個消息項,類等的存在來正確顯示消息。

測試登錄頁面

登錄頁面組件現在可以只是一個容器,所以沒有太多要測試。

<template>
    <b-col sm="6" offset-sm="3">
        <h1><span class="fa fa-sign-in"></span> Login</h1>
        <flash-message />
        <!-- LOGIN FORM -->
        <login-form />
        <hr>
        <login-nav />
    </b-col>
</template>

<script>
import FlashMessage from '@/components/FlashMessage';
import LoginForm from '@/components/LoginForm';
import LoginNav from '@/components/LoginNav';

export default {
    components: {
        FlashMessage,
        LoginForm,
        LoginNav,
    }
}
</script>

何時使用mount vs shallow

關於shallow文檔說:

mount一樣,它創建一個Wrapper ,其中包含已安裝和渲染的Vue組件,但包含已存根的子組件。

這意味着容器組件中的子組件將替換為<!-- -->注釋,並且它們的所有交互性都不會存在。 因此,它將被測組件與其子項可能具有的所有要求隔離開來。

然后,登錄頁面的插入DOM幾乎為空,其中FlashMessageLoginFormLoginNav組件將被替換:

<b-col sm="6" offset-sm="3">
    <h1><span class="fa fa-sign-in"></span> Login</h1>
    <!-- -->
    <!-- LOGIN FORM -->
    <!-- -->
    <hr>
    <!-- -->
</b-col>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM