Previously we've seen how zod can validate incoming HTTP requests and why that's helpful. In this blog post, we explore another use case for zod, in combination with zod-fixture.
zod-fixture is a library that leverages a zod schema to generate new object instances that satisfy the configured schema.
We'll discover why this is useful later, but before we dive into the test cases, let's first define a
zod schema for a
Customer has a few properties and also has child objects for
Now, imagine having a test case that checks if a collection of customers is rendered on the page.
Traditionally, most test cases look like a variant of the next test case. The important part here is at the top of the test, where you create the test fixtures that are used within the test.
I see this a lot in codebases because it's just the simplest way of creating instances, but it also has a couple of drawbacks.
The biggest chunk of the test consists of creating test fixtures, in this case, customer instances.
I didn't even take the time to model the child object
pets because of this.
This makes the test cases look bigger than one would expect.
Because of this, the essence of the test gets lost and it might take a while to understand the test case.
But that isn't the only problem.
What happens when the
Customer model is updated? For example, when a property is added or removed?
With test setups like these, we need to update all the models.
This isn't very pleasant work, and it can also take some time to update.
As a counter measurement, we can introduce model factory methods to create the instances easier.
The refactored test looks as follows.
Instead of manually creating the customer instances, we invoke the
createCustomer method to create a new customer instance.
This is already better, and it solves the mentioned problems, but in my experience, it still isn't ideal.
As developers we tend to over-engineer the boring stuff, so there's a chance that these factories get complicated. More complicated than they need to be.
This beats the entire purpose of readable tests. We don't want to spend minutes figuring out how the setup is done.
For example, we could end up with factories that expect a lot of arguments just to tweak the properties of the model. Or worse, the factory contains complex conditions or structures to create an instance. When this happens, we end up in a similar situation as before, where many test cases need to be updated when we just change the model contract, or the way a model behaves.
Now that we understand the problem, let's see how
zod-fixture, can give us a hand to create test fixtures to keep the test setup as simple as possible.
zod holds the contracts of your models, we can make use of this data to generate the fixtures for your tests.
The easiest way to use
zod-fixture is by invoking the
createFixture method and passing the
zod schema as the argument.
Before we put this in a test, let's first take a look at the output just to give us an idea of what
The first thing you can see is that
createFixture uses the schema to create an instance that complements the zod schema.
What isn't visible from this snippet is that you can't see that it does this randomly.
zod-fixture assigns a random value to all properties, but the shape always looks identical.
For example, a string always has the property name as a prefix, and collections always contain 3 items. This makes it very convenient (and quick way) to new up instances at the start of a test case.
As you can see in the test below, it just takes 1 line of code to generate the
customers test fixture.
We've seen how
zod-fixture helps us to keep the test setup to a bare minimum.
Instead of writing fixtures manually or writing code that does this for us, we can make use of the
createFixture method which does the heavy lifting for us.
zod-fixture, we're also sure that the fixtures that are used within tests are also kept up-to-date with the zod schema, and thus also with the model interfaces that are used within the application.
While this blog post just shows the default behavior of
zod-fixture, it's also possible to change the way how values get generated. If you're interested and what to know more, please take a look at the docs.
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.