Search code examples
javascriptunit-testingyup

How to write unit tests to verify logic in yup schema?


I have some non-trivial validation rules for a Vue app that uses VeeValidate, Yup, and Vitest. I would like to write unit tests for my Yup schema, but the examples that I've seen involve using undocumented APIs (at least from the standpoint of the official documentation on GitHub). Every time I try searching for how to test Yup validations, it refers to the test() method (source), which appears to be a custom validation rather than a unit test.

This isn't the exact schema I'm using, but serves as a simple example:

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

The validation frameworks I've used in other tech stacks allow you to test the result of a single property, so that's the perspective I have walking into testing Yup schemas. I am really new to this, so I could simply be trying to use the framework in a way it was never meant to be used.

My test code:

describe('First name', () => {
    it('cannot be more than 10 characters', () => {
        const model = {
            firstName: '01234567891' // 11 characters, should be invalid
        };

        try {
            schema.validate(model);
        }
        catch (error) {
            // Now I need to assert that firstName is too long
            // and I don't care about the age property in this test

            expect(/* something to identify the first name validation result */)
                .toBe(/* true? A certain message? */);
        }
    });
});

I'm having trouble with the assertion; I have no idea who to write it. Ideally I would like to pass the minimal information to trigger the one validation error I'm interested in (the firstName is too long), but I am getting an error from a property that I don't care about in my test. In my real application, I have a schema with 20+ properties, and it would be a major pain to need to specify 20 valid values and one invalid value in all the tests I need to write.

I am running tests from the command line using npm run test:unit, which is the default setup for Vitest.

How do I write a proper unit test for Yup schemas such that I don't need to specify a huge valid object with one invalid property, and make an assertion for one single validation rule?


Solution

  • This is sort of a face-palm kind of answer, because ViTest allows you to assert that calling a method throws an exception, and it asserts the validation message. This was my first time implementing anything in Vue, and it's been many moons since I wrote unit tests on the frontend. So, for the sake of completeness:

    describe('First name', () => {
        beforeEach(() => {
            this.model = {
                // specify all other required properties here
                // even though I didn't want to.
            };
        });
    
        it('cannot be more than 10 characters', () => {
            this.model.firstName: '01234567891';
    
            expect(() => schema.validateSync(model)).toThrowError('First name must be at most 10 characters');
        });
    });
    

    The functional-style unit tests threw me off a bit. I'm more accustomed to object-oriented unit tests in C#. The fact that Yup throws exceptions when validating an object was also a little jarring, since I'm accustomed to other validation libraries returning a results object instead.

    Even though I didn't want Yup to validate every property, it does, therefore the model being tested must contain all valid data except the one property you expect to be invalid. Then you just need to assert the validation message to ensure the property failed like you thought it would.