Transcript
00:00 A good place to start is to understand how Prisma knows which database to use. That behavior is configured in the file called dot environment that lists all of these environment variables. The relevant one here is the database URL one. You can see that it points to a local file at data dot DB. So this is the database that Prisma uses alongside SQL to perform database operations.
00:23 So if I want to deviate from this behavior and make it right to a different file, I will create another environment file called dot environment dot test and this file will contain all the differences, all the environment variables that apply only in tests. I will copy this variable and I will change the file used for testing to test dot db. I will keep the connection limit parameter set to one because SQLite locks the entire database during write operations and I don't wanna introduce a race condition where one test will be writing something to the database, lock it, and then another test will run-in parallel also attempt it to write something. That'll be terrible. So I'm keeping the connection limit set to one.
01:02 On its own, having this environment. Test file does nothing, I need to load it during the test run. So I will go to the playwright dot config. Ts and here you can see that we're using the dot environment package to load the environment files. So by default it will load this dot environment file.
01:19 Let's opt out of that behavior and import from dot environment an object called dot environment I will call dot environment dot config this will reproduce the previous behavior and load in the dot environment file, the default one, and then I will call the same method again but not provide a different path because I want to load the dot environment dot test file. To make sure that my variables in the dot environment. Test actually override the same variables set in the root environment file, I will provide an option here called override and set it to true. So now when Playwright executes this configuration file, it will pull variables from both of those environment files. And the only thing I have to do now is to also propagate this database URL variable to the application that Playwright spawns.
02:08 So down here, in the web server object, I will go to the environment object and add the database URL and load it from the current process. Database URL. So what I've done here is actually two things. I'm loading my environment dot test file in the Node. Js process that is the Playwright itself, the same process that runs the configuration and the tests.
02:30 And then secondly, I'm also propagating the same database URL change to my application. So so whenever backend logic fires against Prisma to create certain resources or read certain resources, it would know that it has to do so against my test database. If I try running my tests now, they will fail because the test database is not prepared. To prepare it, I will create a global setup file for playwrights in a file called playwrights dot setup dot ts. You can choose whatever name you want.
02:59 So in that file, here I will export a default function called global setup and in this function, I will prepare my test database for the test run. It will consist of two steps. First, I will generate the Prisma client. I will import from node trial process function called spawn synchronously. I will use it to spawn a Prisma process and call the generate with the SQL flag because we're using SQLite in the application.
03:27 And I'll also provide the standard input output option to inherit so I'm able to see all the logs, warnings, and errors that Prisma prints while generating the client. The second step, I want to apply those initial migrations. In a similar fashion, I will call spawn synchronously. I will run Prisma migrate reset then dash dash force and skip seed. So what I'm doing here is I'm forcefully resetting my database to the initial migration defined in the SQL file and then skipping seeding any initial data because I don't need them for my tests.
04:03 And in the same manner, let's set s t d I o to inherit. Since this is a global setup, both of these commands will run before my tests, thus preparing the database and making sure that it's in the right state for the test run. But if I go to the package. Json file and check how Prisma is prepared in the production run, I can see that first we're running migrate deploy and then we generate in the Prisma client. I will align my test setup to have the exact same order so there are less discrepancies between the production run and the test run.
04:33 Now the only thing remaining is to plug this setup file into Playwright. I will head back to Playwright config and add a new option called global setup and point it to the setup file at playwrights.setup.ts. Now let's take a look at the end to end test that we have. Here we have the create notes test from before that uses the authenticate fixture to assume a user persona. In other words, I expect that this user will be created in the test database, alongside the notes because we are trying to create the note as well.
05:04 So let's run these tests and see which database actually gets used. We can see the test succeeding and also that our Prisma preparation steps fire as a part of playwright's run. This is the part of the global setup. And if I go to my project in the Prisma directory, I can now locate this test. Db, this is our test database.
05:29 If I open it, I can observe the resources created. For example, if I go to the user table, this is my user that was created during the authentication in the end to end test. If you take a look at the global setup, we are resetting the database at the first step before the tests run. This means that after a test run, you still have the intact state of the DB if you want to investigate it. So for example, if some of your tests failed, you are able to open this database at the exact state and look around and figure out what's wrong, even if you run this test yesterday.
06:00 So as long as you didn't have any other test runs, the TestDB will always be the latest snapshot of the database, which I find really handy. Naturally, the way you arrange the database in your end to end tests will differ depending on the database that you're using. In our case, we're using Prisma with SQLite which is really handy because SQLite writes the database file to disk so we can point to a different file to use for testing purposes. Now, what you have in your application might be different. But no matter the setup, you still need to make sure to procure proper isolation on the database level.
06:31 So if you are using something like in memory databases, you can prepare a special instance and point your application to use that instance in tests. For dockerized databases like Postgres, you can rely on tools like test containers to spin the test instance of the database and point your application to use that container. Just remember that the goal here is to ensure reliable test runs through database isolation. It will give you both control over the data created during the test run and make sure that no unwanted resources can affect the test results.
