MVVM模式单元测试实战指南
做前端开发这些年,碰过不少架构坑。刚接手一个用MVVM模式写的项目时,最头疼的不是逻辑复杂,而是改完代码不知道有没有搞出新问题。后来尝到了单元测试的甜头,尤其是给MVVM结构写测试,心里踏实多了。
为什么MVVM适合单元测试
MVVM把界面(View)和数据逻辑(ViewModel)拆得清清楚楚。ViewModel不依赖DOM,只管状态和行为,这正好是单元测试最喜欢的类型——纯逻辑、无副作用。比如你写了个购物车功能,点击“结算”按钮前要检查是否登录,这个判断完全可以脱离页面跑测试。
以前团队里有人改了登录状态的字段名,结果首页购物车按钮一直显示“请登录”,其实用户早就登上了。加了测试之后,这种低级错误再也没跑过上线环境。
怎么给ViewModel写测试
以Vue为例,假设你有个简单的表单ViewModel:
const userForm = {
data() {
return {
name: '',
age: null
};
},
computed: {
isValid() {
return this.name && this.age >= 18;
}
},
methods: {
reset() {
this.name = '';
this.age = null;
}
}
};对应的测试可以直接调用方法、检查计算属性:
it('表单验证应正确判断有效性', () => {
const form = userForm.data();
form.name = 'Alice';
form.age = 20;
expect(userForm.computed.isValid.call(form)).toBe(true);
form.age = 17;
expect(userForm.computed.isValid.call(form)).toBe(false);
});Mock服务接口避免网络依赖
真实项目里ViewModel常要调API。测试时可不想真发请求,这时候就得mock。比如登录逻辑调了api.login(credentials),可以在测试里替换成假响应:
jest.mock('../api', () => ({
login: jest.fn().mockResolvedValue({ token: 'fake-token' })
}));这样测“点击登录后token是否保存”就快多了,还不怕接口临时下线影响本地开发。
别忘了监听和事件的测试
ViewModel经常用$emit往外抛事件,比如保存成功后通知父组件刷新列表。这类逻辑也要覆盖:
it('保存成功应触发saved事件', () => {
const mockEmit = jest.fn();
const vm = createComponent({ $emit: mockEmit });
vm.save();
expect(mockEmit).toHaveBeenCalledWith('saved', expect.any(Object));
});这种测试写多了,重构的时候手就不抖了。哪怕把“保存”改成“提交”,只要行为不变,测试照样过。
自动化集成到配置流程
光写测试没用,得让它自动跑。我们在CI配置里加了一行:npm run test:unit,每次提交代码都会执行。新人提交的代码如果没过测试,连合并按钮都点不了。刚开始大家嫌烦,后来发现省了半夜修bug的时间,反而主动写测试了。
软件配置不只是配环境变量和部署脚本,把测试纳入流程,才是让代码长期稳定的正路。