广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

团队必备 | Vitest的10种代码规范

Vitest作为Vue生态中的测试框架,其代码规范直接影响团队协作效率和测试稳定性。我见过很多团队在使用Vitest时,因为没有统一的代码规范导致测试用例混乱、覆盖率不均,甚至出现测试工程被废弃的情况。关键在于测试代码必须像生产代码一样被严格管理,否则测试体系会变成一地鸡毛。在实际项目中,我总结出Vitest的10种代码规范,这些规范不是理

团队必备 | Vitest的10种代码规范
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
Vitest作为Vue生态中的测试框架,其代码规范直接影响团队协作效率和测试稳定性。我见过很多团队在使用Vitest时,因为没有统一的代码规范导致测试用例混乱、覆盖率不均,甚至出现测试工程被废弃的情况。关键在于测试代码必须像生产代码一样被严格管理,否则测试体系会变成一地鸡毛。在实际项目中,我总结出Vitest的10种代码规范,这些规范不是理论,而是踩过坑之后的血泪经验。比如,测试文件命名必须和被测文件一一对应,否则vitest的自动发现机制会出问题。测试用例必须使用async/await,而不是Promise链,这样代码才不会看起来像烂泥。还有,测试异步函数时必须使用done回调,否则测试可能会提前结束。这些细节都会影响测试的覆盖率和可读性。

▌ 技术参考
Vitest的测试文件命名必须严格遵循特定规则,如`MyComponent.spec.ts`。
如果文件名不符合这一规范,vitest将无法正确识别并执行该测试文件。我遇到过一个团队,为了方便直接将测试文件命名为`testMyComponent.ts`,结果导致测试脚本执行失败,覆盖报告里出现大量未执行的测试项。因此,测试文件必须和被测文件同名,且扩展名为`.spec.ts`或`.test.ts`,否则vitest会忽略该文件。这种规范是团队协作中最基础的,必须强制执行。

Vitest支持多种测试类型,包括单元测试、集成测试、端到端测试。
在实际项目中,我建议将单元测试放在`src`目录下,集成测试放在`tests`目录,端到端测试使用cypress或playwright。这样可以避免测试文件混杂,提高代码可读性和项目结构清晰度。如果测试项被归类错误,vitest的执行顺序和报告将变得毫无意义,最终导致测试结果无法准确反映项目状态。

测试用例必须使用async/await,而不是Promise链。
我见过太多团队在写测试时使用Promise.then(),结果代码变得冗长、难以维护。用async/await可以将测试用例写得更清晰,更像生产代码。例如:
```ts
test('should return correct data', async () => {
const result = await fetchData();
expect(result).toBe('expected data');
});
```
这种方式可以让测试代码更易读,也更容易调试。如果依然使用Promise链,测试脚本会变得臃肿,影响团队整体代码质量。

测试异步函数时必须使用done回调或await。
Vitest默认使用Promise-based机制,如果测试函数没有被await,vitest会认为测试已经完成,导致测试失败。我踩过的坑是,某个测试函数没有await,导致测试结果出现“pending”状态,进而误以为测试没有完成。正确的写法是:
```ts
test('should resolve after delay', async () => {
const result = await delay(1000);
expect(result).toBe('done');
});
```
或者使用done回调:
```ts
test('should resolve after delay', (done) => {
delay(1000).then(() => {
done();
});
});
```
两种方式都能确保测试函数正确执行,避免不必要的错误。

测试套件必须使用describe和test结构。
我见过一些团队为了简化代码,直接使用if语句或for循环写测试用例,结果导致测试报告难以理解,也无法正确统计覆盖率。必须使用describe来组织测试套件,test来定义单个用例。例如:
```ts
describe('User Service', () => {
test('should create user', async () => {
// 测试逻辑
});
test('should update user', async () => {
// 测试逻辑
});
});
```
这样结构清晰,也符合vitest的测试报告生成逻辑,让测试结果更直观。

测试代码必须包含足够的断言,避免遗漏关键验证点。
有些团队为了追求效率,只写一个断言,结果导致测试结果不准确。例如,测试一个组件渲染时,仅仅断言是否存在,而没有验证具体内容。我见过一个项目,因为缺少对props的验证,导致测试无法发现关键错误。断言必须覆盖所有关键逻辑,包括props、events、渲染结果等。例如:
```ts
test('should render user name', () => {
const wrapper = mount(UserComponent, { props: { name: 'John' } });
expect(wrapper.text()).toContain('John');
});
```
这样测试才能真正发现问题,而不是走过场。

测试文件必须放在单独的目录中,避免和源码混杂。
我见过很多项目将测试代码和生产代码混放在同一个目录,导致代码混乱,难以维护。vitest推荐将测试文件放在`tests`目录下,这样项目结构更清晰,也方便后续维护。例如,`tests/user.spec.ts`对应的`src/user.ts`,这种结构可以避免测试代码被误修改或误提交。如果测试文件和源码混在一起,调试和版本控制都会变得困难。

测试覆盖率必须设置合理的阈值,避免盲目追求100%。
我见过一些团队设置覆盖率目标为100%,结果导致测试冗余,甚至引入多余逻辑。正确的做法是将覆盖率设为80%-90%,这样既能保证核心功能测试充分,又不会让测试变得臃肿。例如:
```ts
// vitest.config.js
module.exports = {
coverage: {
threshold: {
functions: 90,
lines: 90
}
}
};
```
这样设置可以过滤掉一些不重要的分支,避免测试报告失去意义。

测试用例必须使用beforeEach和afterEach进行初始化和清理。
我遇到过一个项目,因为没有正确清理测试环境,导致后续测试用例出现污染,结果错误频繁出现。例如,mock数据没有被重置,导致测试结果异常。正确的做法是使用beforeEach和afterEach来重置状态,确保每次测试都是独立运行的。例如:
```ts
beforeEach(() => {
jest.clearAllMocks();
mockData = { name: 'John' };
});

afterEach(() => {
mockData = {};
});
```
这样可以避免测试用例之间相互干扰,提高测试的准确性和可靠性。

测试框架必须使用jest或vitest的mock机制,而不是手动模拟。
我见过一些团队为了追求简单,直接使用console.log调试,结果导致测试无法自动化。正确的做法是使用vitest的mock函数,例如:
```ts
jest.spyOn(axios, 'get').mockResolvedValue({ data: 'mock response' });
```
这样可以精确控制mock行为,提高测试的可控性。如果仍然使用mock.js或其他工具,可能导致测试依赖外部库,增加构建复杂度。

测试用例必须使用only标记来聚焦关键测试项。
在开发过程中,我经常遇到大量测试用例,但某些关键用例需要优先执行。使用only标记可以确保这些用例被优先运行,同时忽略其他非关键项。例如:
```ts
describe('User Service', () => {
test.only('should create user', async () => {
// 测试逻辑
});
});
```
这样可以节省时间,提高测试效率。

测试环境必须使用dotenv加载配置文件,而非硬编码。
我见过很多团队在测试环境中直接写环境变量,结果导致测试环境和生产环境配置混乱。正确的做法是使用dotenv加载`.env.test`文件,例如:
```ts
// vitest.config.js
module.exports = {
env: {
API_URL: process.env.VITE_API_URL
}
};
```
这样可以确保测试环境使用正确的配置,同时避免敏感信息暴露在代码中。

测试代码必须保持简洁,避免冗长的setup。
我见过一些测试用例包含大量的setup逻辑,导致测试代码难以阅读。正确的做法是将setup逻辑封装到独立的函数中,例如:
```ts
async function setup() {
const wrapper = mount(UserComponent, { props: { name: 'John' } });
return wrapper;
}

test('should render user name', async () => {
const wrapper = await setup();
expect(wrapper.text()).toContain('John');
});
```
这样可以提高测试代码的可读性,也方便后续维护和复用。

测试函数必须使用合适的描述信息,避免模糊。
我见过很多测试用例的描述信息只写“测试函数”,结果导致测试报告无法清晰区分问题。正确的做法是使用具体描述,例如:
```ts
test('should throw error when invalid input', () => {
expect(() => createComponent({ name: '' })).toThrow('Name is required');
});
```
这样可以让测试报告更直观,也方便后续排查问题。

测试项必须按照模块划分,而不是按照功能。
我见过一些团队将测试项按照功能分类,导致测试报告结构混乱。正确的做法是按照模块划分,例如:
```ts
describe('User Module', () => {
// 所有和用户相关的测试用例
});
```
这样可以确保测试结构清晰,也方便团队后续维护和扩展。