Loading
Current section: Context 4 exercises
solution

Automatic Fixtures Solution

Enable automatic Vitest fixtures with auto: true to ensure setup and cleanup always run—ideal for mocks like databases or APIs.

Loading solution

Transcript

00:00 To change the way Vitas deploys your fixture, you need to change its declaration. There are two main ways to declare a fixture. If you take a look at this CreateMock database, it uses a function to describe how this fixture is supposed to work. But there is another way. You can declare your fixtures in this format, fixtureName, and as the value you can provide an array,

00:18 where the first entry to this array will be the function, the fixture implementation, and the second would be this options object. You can use these options to customize the behavior of your fixture. In fact, let's rewrite our existing CreateMock database function to use this format.

00:32 So I'll copy the name and provide an array here and provide an options object over here. And then I will convert it to an arrow function and save. So with these changes, let's head to the options objects and explore it.

00:50 In this object, we can provide a property called auto and set its value to true. This will tell Vitas to automatically apply this fixture. Basically, use it all the time. So call it preparation and cleanup steps, regardless if this fixture is being referenced in your tests. Now let's explore this in action. If we head to our tests, we have a single test case right now

01:09 that should return undefined if we're querying for a non-existing user. And if I run this test with npm test, finally it passes. It passes because although we're not referencing our CreateMock database fixture here, it's still getting run because it's an automatic fixture now. Now let's finish this test suite by introducing one more test case. We're actually fetching the user.

01:29 I will add a new test case and call it returns the user by ID, provided the test function, and grab our CreateMock database fixture from the test context. Then I will use this fixture like so. This fixture works in a way that it accepts a callback and you can modify your mock database within that callback. So here I'm running an operation that inserts

01:50 a new entry into the users table with this particular values. So abc123 as the user ID and johnMaverick as the user name. Now all that's left to do is to call my query user function. So I will write my expectation. I expect the query user by abc123 resolves to equal this user object like this.

02:11 So basically when I'm querying the user by the ID that I have populated in my mock database, I expect to receive the correct user object. To verify this, let's run this test. So once again, npm test. And we can see both the scenarios passing. So what happens here is that our CreateMock database fixture gets applied in test case that doesn't rely on it,

02:31 and also in another test case that actually uses it to model the right database state to test our functionality. When creating your fixtures, bear in mind that not every fixture has to be automatic. In fact, it's a very good default that fixtures are lazy and you can always opt out from them by providing this auto equals true option next to your fixture definition. And one last thing I want to show you

02:51 is how to design your fixtures for failures. This is what I mean. If we head back to the test case and change our expectation, for example, changing the surname of this user, and rerun the test, we will see that the test is failing. But using custom fixtures allows you to craft the custom failure experience as well. If I scroll up, I will see that yes,

03:11 my assertion here failed. But a little bit below here, I have a custom message printed by my fixture that says, hey, this is the state of your mock database. So you can just click and explore it, debugging why the data was not what you expected in tests. Designing your test experience around failures is extremely powerful because when tests fail, you want to make sure that you spend

03:31 as little time as possible getting to the root cause of it. And one of the ways to improve that debugging experience is also through custom fixtures. If you're curious how I achieved this error experience, I'm using the onTestFailed function. This is a built-in hook from VTest and you can grab it from the test context over here. And then using this hook, I'm checking what is the state of my test.

03:50 And if it passes, I'm deleting the mock database from the disk. But if it fails, I'm pushing a new error to the VTest test report and printing the path to my mock database without deleting it. Feel free to explore the implementation of this fixture and use it as a reference when creating your own.