You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为Yup Schema编写单元测试:实现单属性规则验证且无需构造完整有效对象

如何为Yup Schema编写单元测试:实现单属性规则验证且无需构造完整有效对象

嘿,我完全懂你的痛点——面对20多个属性的Schema,每次测试都要凑出一个完整的有效对象,只改一个字段来验证规则,这实在太折腾了!而且之前用schema.validate()会触发所有字段的校验,导致无关错误干扰测试,完全不是我们想要的单元测试方式。

其实Yup早就提供了专门解决这个问题的方法:validateAt(异步)或者validateAtSync(同步)。这个方法允许你只针对单个字段进行验证,完全不用管其他字段的状态,完美匹配你想要的单属性规则测试需求。

核心解决思路

validateAt的用法非常直观:它接受两个参数,第一个是你要测试的字段名(比如'firstName'),第二个是包含该字段的对象(甚至可以只传这个字段,其他字段都不用填)。它只会验证指定的字段,不会触发其他字段的校验规则,彻底解决了构造完整对象的麻烦。

需要注意的是,Yup的验证默认是异步的,所以测试里最好用async/await来处理,或者用Vitest的.rejects断言简化代码。

针对你的示例写具体测试代码

先补全你的Schema定义(记得导入yup):

import * as yup from 'yup';

const schema = yup.object().shape({
    firstName: yup.string().required().max(10),
    age: yup.number().required()
});

接下来是两种常用的测试写法,你可以选自己习惯的:

方式1:用.rejects断言简化测试

这种写法最简洁,直接断言验证会抛出符合预期的错误:

import { describe, it, expect } from 'vitest';
import * as yup from 'yup';

describe('First name validation rules', () => {
    it('cannot be more than 10 characters', async () => {
        // 只传要测试的firstName,其他字段完全不用管
        await expect(schema.validateAt('firstName', { firstName: '01234567891' }))
            .rejects.toThrow(/firstName must be at most 10 characters/);
    });

    it('is required', async () => {
        await expect(schema.validateAt('firstName', { firstName: '' }))
            .rejects.toThrow('firstName is a required field');
    });
});

describe('Age validation rules', () => {
    it('is required', async () => {
        // 甚至可以传空对象,测试age的必填规则
        await expect(schema.validateAt('age', {}))
            .rejects.toThrow('age is a required field');
    });
});

方式2:捕获错误后精确断言

如果需要更细致的验证(比如确认错误来自哪个字段、消息是否完全匹配),可以用try/catch捕获Yup.ValidationError

import { describe, it, expect } from 'vitest';
import * as yup from 'yup';

describe('First name validation rules', () => {
    it('cannot be more than 10 characters', async () => {
        try {
            await schema.validateAt('firstName', { firstName: '01234567891' });
            // 如果没抛出错误,就标记测试失败
            expect.fail('Expected validation to fail for long first name');
        } catch (error) {
            // 断言错误是Yup的ValidationError类型
            expect(error).toBeInstanceOf(yup.ValidationError);
            // 断言错误来自firstName字段
            expect(error.path).toBe('firstName');
            // 断言错误消息符合预期
            expect(error.message).toBe('firstName must be at most 10 characters');
        }
    });
});

同步验证的写法

如果你偏好同步逻辑,也可以用validateAtSync方法,用法和validateAt完全一致,只是它会同步抛出错误,不需要await:

it('cannot be more than 10 characters', () => {
    expect(() => schema.validateAtSync('firstName', { firstName: '01234567891' }))
        .toThrow(/firstName must be at most 10 characters/);
});

总结一下

  1. validateAt/validateAtSync替代validate,实现单字段独立验证,不用再构造完整对象。
  2. 用异步测试的async/await或者.rejects断言处理异步验证逻辑。
  3. 可以通过断言错误类型、字段路径或错误消息,确保你的验证规则完全符合预期。

这样你就能轻松为每个字段的每条规则编写独立的单元测试,再也不用为填充无关字段而头疼了!

备注:内容来源于stack exchange,提问作者Greg Burghardt

火山引擎 最新活动