Laravel Testing Overview 0:00Let's take some time to give you an introduction to Laravel's testing facilities, which are really, really good. In fact, I would say they're best in class. So out of the box, we can see a feature directory. This is for the the larger features. What does your application actually do? What are its features? And then you have the lower level unit. And of course, you can create any number of folders here to fit your structure. So if we take a look at a unit test, a simple assertion. Now, Laravel includes PHP unit as a dependency. So you should be able to type PHP unit to trigger this. But if that doesn't work, make sure you have vendor bin in your path. Otherwise, just type it out like this. Okay, let's take a look at the feature. Now, a basic test, we can see that we are making a GET request, and then asserting that the response is a status code of 200. So I can trigger this by saying test feature example. And there we go. We could even assert Writing Feature Tests 0:44are making a GET request, and then asserting that the response is a status code of 200. So I can trigger this by saying test feature example. And there we go. We could even assert that we see something. For example, let's let's do this, make a GET request to the home page and assert that we see. Well, let's switch over. How about we just want to assert that we see that on the home page. Alright, let's run it again. And it passes. So that can be useful for basic things where visit a page. And I just want to make sure that I see this. Now, if you actually need to open a browser, because you will be testing JavaScript behavior, you can review Laravel desk. And we've already covered that at Laracast. So just hunt around. And you can learn about that if you need to. Okay, but what about other things? What about lower level things? For example, remember this post method that we had in the last episode, where we fetch the archives? Well, what Planning Archives Unit Test 1:28if you need to. Okay, but what about other things? What about lower level things? For example, remember this post method that we had in the last episode, where we fetch the archives? Well, what if we want to write a test to ensure that this always works? And that way, six months down the line, if we refactor and we change something, we will have immediate verification as to whether that refactor worked. Okay, let's give it a shot. Let's go back to tests, unit, we'll store it here. A lot of people disagree on what a unit test is. Some would say it is the smallest layer you can possibly test. So the idea of hitting a database is terrible and not a unit test. It's an integration test. Other people would say, sure, if it's fast, who cares? So as such, I don't really like to think of unit versus integration versus functional versus acceptance. Nobody agrees on it anyways. So I even like the idea of thinking of your small tests, your medium tests, and your large tests.of unit versus integration versus functional versus acceptance. Nobody agrees on it anyways. So I even like the idea of thinking of your small tests, your medium tests, and your large tests. Keep it simple. Anyways, with that in mind, I want to test that performing a call to this method is going to return what I want. So the way I set this up is doing a typical given, when, then. Let's imagine, given I have two records in the database that are posts, and each one is posted a month apart. All right, so we have one post from this month and another post from last month. Well, when I fetch the archives, then what do we expect to happen? Well, then the response should be in the proper format. Very easy. So we can see that this sets up the world for our test, this hits the method or performs the action, and then this creates the assertion. So it sounds like when I fetch the archives, well, that means when I call this method, post archives. And let's go Using Model Factories 3:10this hits the method or performs the action, and then this creates the assertion. So it sounds like when I fetch the archives, well, that means when I call this method, post archives. And let's go ahead and import that at the top. Okay, so how do we set up the world? Well, as it turns out, LayerVal offers something called ModelFactories to really help with this. Take a look. If we go to Database, Factories, ModelFactory, you'll see that we have one out of the box. So think of the factory as like the blueprint for an eloquent model. And this way, we can quickly whip up new records, even save them to the database, but use dummy data. Here's how we reference it. phpArtisanTinker, and we can use a general factory function here. So I could say app user and make me a database factory. So now you'll see it gets a fake name and a fake unique email. But it doesn't persist it. If I do want to persist it, I can say create instead. And now we have a new user.a database factory. So now you'll see it gets a fake name and a fake unique email. But it doesn't persist it. If I do want to persist it, I can say create instead. And now we have a new user. You can even take this further and say, give me 50 different users. And we get that. Very, very cool, isn't it? And you can even use this not just for testing, but for general database seeding if you want. All right, let's copy this and create a new one for a post. Now, what does a post consist of? Well, if we switch back real quick to our migration, a post consists of the user who created it, a title, and a body. Okay, so a title can be a fake sentence. The body can be a fake paragraph. And then finally, the user ID. Well, we could hardcode one. Let's stick with that for now. And if we give this another shot, I'm going to say, give me a factory for a post this time, and whip one up. And there we go. We have dummy data. But yeah,one. Let's stick with that for now. And if we give this another shot, I'm going to say, give me a factory for a post this time, and whip one up. And there we go. We have dummy data. But yeah, the user ID, we want to make sure that an actual user is associated with that. So let's make that equal to a function that Laravel will trigger. And here, I'm going to manually whip up a user. Create a new user, persist it, and then return the ID field to me. And that will be assigned here. So let's give that another run. And there we go. It works. So now, we can make use of this in our tests. For example, I could say, given that I have a post that was created this month, and we'll say, first, but also we need a post that was created last month. So we can override the defaults by passing an array here. And I could say, created at, and then we'll say, created at is going to be equal to, and let's use carbon. Carbon now, and I'm going to subtract Creating a Test Database 5:45the defaults by passing an array here. And I could say, created at, and then we'll say, created at is going to be equal to, and let's use carbon. Carbon now, and I'm going to subtract one month. So one post created this month, and another post created last month. So given that exists in the database, when I fetch all of the archives, well, at the very least, then I expect to have an array of two items. So let's say, assert count two, and compare that against the posts that were returned from this call. But now, we have a new problem. If I run this test, it's going to run it on my local database. And my local database may have 50 posts, because I'm testing it locally. I really want to be using a custom test-specific database. So let's do this. Let's open up MySQL, and I will create a new database called blog testing. There we go. Next, I'm going to switch back, and if we go to our env file, you can see that rightSo let's do this. Let's open up MySQL, and I will create a new database called blog testing. There we go. Next, I'm going to switch back, and if we go to our env file, you can see that right down here, we're being explicit about the name of the database. But for our testing, I want that to now reference our new database. So we're going to go to phpunit.xml, and if we scroll down, you'll see here are phpunit-specific overrides. So for example, here, we have app environment set to local, but in our phpunit test execution, we're going to update that to be testing. So let's do this. Let's duplicate it, and now I'm going to set the database name to testing. But that's still not going to work just yet. If we give it a run, though, it fails, and we can see that we get a big error. So let's look for the message. Table posts does not exist. Table posts does not exist. So that's because we created a new database, but we haven't migratedand we can see that we get a big error. So let's look for the message. Table posts does not exist. Table posts does not exist. So that's because we created a new database, but we haven't migrated it yet. Now, we can do that in a couple ways. One would be to go to our config slash database file and create a brand new connection, and then reference that when we migrate a database. But another option, just very, very quick, is to go to your env file, change it to your test database name, and then run phpArtisanMigrate. So now it's going to use the test database, and then you just backtrack. That's kind of a quick and dirty way to deal with this. Another option you'll find is that you can reference the database migrations trait, and we'll talk about that later. But nonetheless, if we switch back to SQL Pro, we can see I'm in my blog testing database, and here's our fresh install. All right, so let's give this a run Resetting Database Between Tests 8:06and we'll talk about that later. But nonetheless, if we switch back to SQL Pro, we can see I'm in my blog testing database, and here's our fresh install. All right, so let's give this a run again. Test unit example test. And yes, it works, but if we come back, now we have two posts. And if I run it a few more times, you're going to see that every time we add more records to the database. And that's not what we want. We don't want to build up our test database over and over. Ideally, I'd like to clear everything out when I'm done. So here's what we can do. If we switch back, let's close a lot of this out. At the very top, you see that we pull in two traits, one for running our migrations and then rolling everything back after the test, and then another one for transactions, which I quite like. Database transactions run your tests through a transaction. So all of this stuff, it's going to add some records, but then when theand then another one for transactions, which I quite like. Database transactions run your tests through a transaction. So all of this stuff, it's going to add some records, but then when the test is finished, it rolls back that transaction. So it essentially undoes everything that you just did. Now, as many times as we run this, it doesn't matter, at the end of every test, we roll it back. So if I give this a refresh, we don't have any lingering records. Okay, great, but everything is passing. Okay, great. So now we can continue on. Given I have a world that consists of two posts created one month apart, when I fetch the archives, yes, I expect two records, but let's be a little more specific. I'm going to say assert equals, and it should be an array of arrays, right? So we will compare that against the posts. Okay, let's first, we've already written this out, so let me show you what the format will be in. All right, so yeah, you'll have two arrays forright? So we will compare that against the posts. Okay, let's first, we've already written this out, so let me show you what the format will be in. All right, so yeah, you'll have two arrays for each item contains the year, the month, and the published. So let's just grab that, paste it in. But now, I don't want to hard-code this, because when I run the test in a few months, this will not work because I hard-coded it. So luckily, we saved these two as variables, so instead I could say, first, createdAt, and then give me the year. Remember, createdAt, that will be an instance of Carbon. Next, for the monthName, yeah, we could do the same thing. createdAt, format, and then I'm going to use the letter F, and that will convert it to the name of the month. Finally, published will be set to one. All right, so we can do one more here, and this one will refer to our second record. So this should be the format that's returned.of the month. Finally, published will be set to one. All right, so we can do one more here, and this one will refer to our second record. So this should be the format that's returned. Let's run it, and we get green, so we know this works. And that means for the lifetime of this project, if you ever need to refactor the archives, well, maybe you forget to do something, or you rewrite the query in regular SQL, but you forget to order by. Well, it's going to catch that, and it's going to blow up, and you'll know, something went wrong, I need to backtrack. Very, very cool. So that's been an introduction to testing 101. I know it may seem a little overwhelming if this is new to you, and do not feel like you immediately have to start doing this, or you're a bad developer. But I can assure you that the more you do this, the easier it is. So if all of these were new concepts, yeah, it may take you a little while, but the more you do it,or you're a bad developer. But I can assure you that the more you do this, the easier it is. So if all of these were new concepts, yeah, it may take you a little while, but the more you do it, it's just going to be basic common sense, and it'll even be fun. Given I have this world, when I perform this action, then this should be the result of that action.