You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Vue Test Utils测试登录表单提交是否成功?

问题

我想知道如何测试登录是否成功?我有一个Login组件:

<template>
    <NavBar></NavBar>    
    <div id="login">
        <h1 style="text-align: center;">Login</h1>
        <form class="form">
            <label class="form-label" for="username">Username</label>
            <br/>
            <input class="form-field" type="text" name="username" v-model="input.username" placeholder="Username" />
            <br/>        
            <label class="form-label" for="password">Password</label>
            <br/>
            <input class="form-field" type="password" name="password" v-model="input.password" placeholder="Password" />
            <br/>
            <div style="text-align: center;" >
                <button class="form-field" type="button" v-on:click="login()">Login</button>
            </div>
            
            <br/>
            <span v-if="status==1">Authenticating...</span>
        </form>
        <div id="powered">
            Powered by <a href="#">TinyDrive</a>
        </div>
    </div>
</template>

<script>
import { store } from '../store';
import { api } from '../api';
import NavBar from '../components/NavBar.vue';

export default {
    name: 'Login',
    components: {
        NavBar
    },
    data() {
        return {
            input: {
                password: '',
                username: '',                
            },
            status: 0,
        };
    },
    methods: {
        async login() {
            if (this.input.username != "" && this.input.password != "") {
                this.status = 1; //loading
                const response = await api.login(this.input.username, this.input.password);
                if (response.status == true) {
                    store.setToken(response.token);
                    store.setAuthenticated(true);                    
                    this.$router.replace({ name: "filemanager" });
                } else {
                    console.log(response.message);
                }
                this.status = 0; //default
            } else {
                console.log("A username and password must be present");
            }
        }
    }
}
</script>

我希望测试登录成功后渲染内容是否发生变化,编写了如下测试代码片段:

test('submit the form', async () => {
    const wrapper = mount(Login, {
        global: {
            plugins: [router]
        }
    }) 
    
    expect(wrapper.html()).to.contain('class="form-field" type="text" name="username" placeholder="Username"')

    let username = wrapper.find("input[name=username]")
    username.element.value = 'test';

    let password = wrapper.find("input[type=password]")
    password.element.value = 'test1234';

    await wrapper.find('form').trigger('submit')
    
    //TODO: how do I test if either the rendered result or the current route?
})

请问该如何完善测试代码,验证登录成功后的渲染结果或当前路由变化?


解决方案

1. 修正测试触发逻辑

你的登录按钮是type="button",绑定的是click事件,触发表单submit不会触发登录逻辑,需直接触发按钮点击。同时,修改输入框值后要同步v-model(用setValue方法自动触发input事件,而非直接修改element.value)。

2. Mock API 调用

测试时不能依赖真实后端接口,需mockapi.login返回成功结果:

import { api } from '../api';
jest.mock('../api', () => ({
  api: {
    login: jest.fn().mockResolvedValue({ status: true, token: 'fake-token' })
  }
}));

3. 验证路由变化

可以通过检查router.currentRoute或断言路由replace方法的调用情况:

// 直接检查当前路由名称
expect(router.currentRoute.value.name).toBe('filemanager');

// 或用jest spy断言replace方法被正确调用
const replaceSpy = jest.spyOn(router, 'replace');
expect(replaceSpy).toHaveBeenCalledWith({ name: 'filemanager' });

4. 验证Store状态更新

通过jest spy监听store的setToken和setAuthenticated方法,确认登录成功后状态被正确设置:

import { store } from '../store';
const setTokenSpy = jest.spyOn(store, 'setToken');
const setAuthenticatedSpy = jest.spyOn(store, 'setAuthenticated');

// 登录后断言
expect(setTokenSpy).toHaveBeenCalledWith('fake-token');
expect(setAuthenticatedSpy).toHaveBeenCalledWith(true);

5. 验证渲染内容变化

登录成功后Login组件会被路由卸载,可断言组件内的登录表单元素不再存在:

expect(wrapper.find('input[name=username]').exists()).toBe(false);
// 或直接断言组件已被销毁
expect(wrapper.exists()).toBe(false);

完整测试代码示例

import { mount } from '@vue/test-utils';
import Login from '../components/Login.vue';
import router from '../router';
import { api } from '../api';
import { store } from '../store';

// Mock API 登录接口
jest.mock('../api', () => ({
  api: {
    login: jest.fn().mockResolvedValue({ status: true, token: 'fake-token' })
  }
}));

test('successful login redirects to filemanager and updates store', async () => {
  // 监听store和路由的关键方法
  const setTokenSpy = jest.spyOn(store, 'setToken');
  const setAuthenticatedSpy = jest.spyOn(store, 'setAuthenticated');
  const replaceSpy = jest.spyOn(router, 'replace');

  // 挂载Login组件
  const wrapper = mount(Login, {
    global: {
      plugins: [router]
    }
  });

  // 设置输入框值并同步v-model
  await wrapper.find('input[name=username]').setValue('test');
  await wrapper.find('input[type=password]').setValue('test1234');

  // 触发登录按钮点击
  await wrapper.find('button').trigger('click');

  // 验证API调用参数正确
  expect(api.login).toHaveBeenCalledWith('test', 'test1234');

  // 验证Store状态更新
  expect(setTokenSpy).toHaveBeenCalledWith('fake-token');
  expect(setAuthenticatedSpy).toHaveBeenCalledWith(true);

  // 验证路由跳转
  expect(replaceSpy).toHaveBeenCalledWith({ name: 'filemanager' });
  expect(router.currentRoute.value.name).toBe('filemanager');

  // 验证登录组件已卸载
  expect(wrapper.exists()).toBe(false);
});

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 09:34:54