Seeders vs migrations0:00
In our application, we will need courses and videos in the database. And this is not user-generated data, so this means we have to take care of adding those items to the database. And there are two main approaches for that. The first and most common approach is through Laravel seeders. So already inside a Laravel application, we have one seeder, which is the DatabaseSeeder, which will be run if we're going to call php artisan db:seed, like this. And this is one way to add data to our database. But it gets a little bit tricky when I think about when are we going to run this command. So this means we have to tell our team or other users that when they install our application, they have to run this, and we need to take care that this is going to run on the server,
So this means we have to tell our team or other users that when they install our application, they have to run this, and we need to take care that this is going to run on the server, we're going to deploy it, but only once and then not anymore. So here it is a little bit tricky when and how to run them, and you need to communicate that. I like it more when things are way more clear. So the second approach you probably also have heard of is through migrations. So yeah, typically migrations are only for working on the database schema, adding new tables, adding new columns, removing one, changing things. But yeah, some do always add data to the database through migrations. The big advantage of migrations is that they are organized.
But yeah, some do always add data to the database through migrations. The big advantage of migrations is that they are organized. So they run automatically in a specific order and to be concrete in the order that you create them. A downside of using migrations to add data is that every time we use the RefreshDatabase trait, for example, inside our test, that we already have data inside our database, even though we didn't create any factories ourselves. And this can get quite complicated if at this point where we're going to start the test, we already have data inside our database, but we don't know which data it is. So one way to handle this is by, for example, inside a migration like here, I could use an environment check and only add specific data.
So one way to handle this is by, for example, inside a migration like here, I could use an environment check and only add specific data if we are currently in the testing environment or only adding them if we are on production server. So this works, but it gets a little bit messy here if you have code about your tests inside your migrations, which I actually don't like. Okay, so this means those two approaches, seeders and migrations, don't really work for me here. So what would be a better option? So what we can do is we can mix it somehow. So let me show you.
Writing seeder tests2:27
don't really work for me here. So what would be a better option? So what we can do is we can mix it somehow. So let me show you. And of course, we're going to start by creating a new test. And we keep it quite simple by calling it DatabaseSeederTest. Okay, so I want to write now some tests to make sure that after we've seeded, after we've put our data inside the database, that we see it there. And we're still going to use seeders, but a little bit differently. But yeah, let me show you. But first, we're going to start thinking about what we want to test. First, we want to make sure it adds given courses.
But first, we're going to start thinking about what we want to test. First, we want to make sure it adds given courses. So we're going to start with a few courses, and we want to make sure that we see them. And second, we want to make sure it adds given courses only once. So this will be quite interesting. I'll tell you more about this just in a minute. And then we also want to make sure we are going to add the videos. It adds given videos. And let's also add another one here that this only happens once.
It adds given videos. And let's also add another one here that this only happens once. And I will tell you why in just a few seconds. It adds given only once. Okay, we're going to start with the first one. So first, we're going to start with an assert database count for the courses table. We want it to be empty. Of course, I already know that through the refresh rate, everything will be empty inside my database.
Of course, I already know that through the refresh rate, everything will be empty inside my database. I think it's a good idea to make it very clear inside the test if you only think about this one what we are doing. First, we want to make sure there are no courses in the database. Then we make our action, and then we're going to make sure that we have more of them. And the action will be our database here. So we're calling the php artisan db:seed like this, yep.
So we're calling the php artisan db:seed like this, yep. And after running this, we want to make sure that we see something. So first, we want to make sure that we see three Course instances. And then maybe it's also a good idea to check the titles, just to make sure that we have the right Course instances. And we're going to use assertDatabaseHas. Again, we're going to use the Course class as the first argument. And second, we now have here an array. We're going to say we want to make sure that there is a Course with a title.
And second, we now have here an array. We're going to say we want to make sure that there is a course with a title. And here can already tell a little bit more about the dummy courses, which we have. And the first one is Laravel for beginners. And we want two more. So the second one will be advanced Laravel. And the third one will be TD the Laravel way. Of course, these are all dummy courses, but we just need something to work with here inside our demo application.
Creating course seeder5:16
Of course, these are all dummy courses, but we just need something to work with here inside our demo application. And I think this looks already good. All right, when we're going to run this, it will probably fail. Fill that table courses matches entries of three, entries found zero. So no courses added. And yeah, we haven't added anything through any database either. So let's create a new one. And we call it addGivenCourses either. Like this, of course, if we run this now again,
And we call it addGivenCourses either. Like this, of course, if we run this now again, nothing is going to happen. So this means we're going to need to add our courses here. Okay, so let's bring in here the code for creating courses. I don't want you to watch me doing all of this. That's why I have already prepared this. And we create some courses. We need to import the models and also the string helper, which we need here for creating the slack.
We need to import the models and also the str helper, which we need here for creating the slack. And here we create three of the courses, which I just showed you before. So if we run php artisan test now again, it will still fail. Now it's because inside our database seeder, right here we haven't called our new seeder yet, because this is the method which is going to be called when we run php artisan db:seed. So here we can make use of calling a specific seeder. And our one is called AddGivenCourseSeeder.
So here we can make use of calling a specific seeder. And our one is called AddGivenCourseSeeder. And we're going to call the class. We don't return anything. And I also don't need the block anymore. All right, let's run test again. And now it's passing. All right. So this means we have now made sure that after running our seed command, that we have three courses inside the database.
Ensuring seeding idempotency6:55
So this means we have now made sure that after running our seed comment, that we have three courses inside the database and the ones with this specific title. All right, let's move on to the second test here. And actually, I can copy a little bit of this here already. And I need this here. And here when we're going to act, I'm now going to call this comment twice. So this means I want to make sure no matter how often this comment is called, the data only gets added once.
So this means I want to make sure no matter how often this comment is called, the data only gets added once. And you will see why we're doing this. And then we want to make sure that we still have only three courses inside the database. So like this. So now it should fail because now it should add six of them. Yes, six. All right, so what can we do about this? We want to make sure it doesn't matter how often we call this comment here.
All right, so what can we do about this? We want to make sure it doesn't matter how often we call this comment here that we only add the courses once. And inside my courses here, if we scroll down here, you will see a method which I haven't showed you yet, which I added, which is called isDataAlreadyGiven. So this means I'm going to add here a check if the data for this currency is already given in the database. And if not, then we're going to run it. And if not, we don't run it.
And if not, then we're going to run it. And if not, we don't run it. So this means you were checking for the titles if they all exist, all those courses. So this means if we go up here, we're going to make a check first. And here I'm going to see if this data is already given. And in the case it is already given, we're going to just return because we don't want to run it like this. Yep. And if we run our test again, this should now pass.
Yep. And if we run our test again, this should now pass. And yes, it does. And I think this is a very clever technique here where we're now making sure that the CD is only going to run for a specific case. And in this case, it is the case where we don't have the courses yet inside the database. And this now means that I can run this CD every time with every deploy, and we're just making sure that the data is given as we expected.
and we're just making sure that the data is given as we expected.
