Laravel 5.8 Tutorial From Scratch - e49 - Testing 101 Using PHPUnit

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this episode we're going to start to tackle the basics of unit testing or feature testing your application for some of you this may be an entirely foreign concept we're gonna cover the basics of it and it does take some time to get into the workflow of actually testing your application so in a normal flow you would normally write some code and then you manually test that it does what you think that it's supposed to do this is what we would consider basically manual testing would it be great if you could write tests for your entire application that tested every single feature from top to bottom and then you can automate it and just run that over and over as you're changing code making sure that you haven't broken something else I mean how many times have you made a change and then what's something that seemed completely unrelated bricks and you're not quite sure why if you change this one thing why something on the other side of the application broke well of course everything has consequences but when we are manually testing it's a very focused kind of testing but we are only really concerned with what we are changing however when you change your code that are repercussions and so because of that it could damage another part of your application and this is where testing really shines because testing because it's automated it can run through every single test and test everything in your application in a single shot which is something that is completely impossible to do in a manual way so if you've been following along the entire time with this course then you do know that we installed telescope if you installed telescope in your project telescope needs to be disabled before we can actually run this so these next couple of steps are only for those of you that have telescope installed so let me jump into the terminal and let's run vendor bin slash PHP unit this will run PHP unit now out of the box PHP unit is set up with level and PHP unit is the library that we're gonna use for testing our application however if you run this and you have telescope it will not work it will fail and the reason for that is due to recent changes with the environment file and what we really need to do is just disable it for testing you really don't need telescope at all for testing so let's go back to phpstorm again this is only if you have this installed in your machine so in the root directory let's go to a file called PHP unit XML and let's add a new line here for telescope underscore enabled and let's set that to false hit save then go back and let's run vendor bin PHP again and if you get green great we are good to go so it says that it ran two tests and it says that it had two assertions assertions are when you are basically comparing what you expect something to be against what your application gave you back for example you may say well if I do this then I expect to see this particular record in the database so we would say that's an assertion it's just a word for saying that you made sure that something was true or equal or null or whatever the comparison is that was true so that is an assertion now each test is just a function let me show you let's go back to phpstorm and let's go into our test directory now this is the nice thing with letter well everything comes set up out of the box so inside tests you do have a features folder and a unit folder for now we're just gonna worry about the features directory let's open up this example test dot PHP so this right here is a test and the way a test is written is that it either needs to start with the word test or it needs to have a special annotation up here at the top something like this when you have this special annotation then you can just write whatever method name you want now me personally I do like my method names basically being very large very verbose because you really do want to know what that test is supposed to be testing so something like test one that's not good enough for a testing it needs to be something like a new user gets an email when it registers that is a proper function name for a test anything that is not descriptive of what that test does is not good enough for testing so just make sure you have nicely written tests so we have this example test it doesn't really do anything but we do see that it tries to do a get request to slash so basically our home page and it just makes sure that we get a status of 200 let's go ahead and change this file up I want to put one of our controllers under a test so let's work through this together let's open up our HTTP and the controller that I'm thinking of is my customer controller obviously my customer control is nice and clean and everything in here I believe right now does everything that it's supposed to do so this is what we would call backfilling the tests typically you would write the test alongside with your actual implementation but because we were not talking about testing we're gonna add the back fill a test for the customers controller so I want to write a couple of tests to test what customers controller is actually doing so let me go ahead and rename this test right here let's say refactor rename just name it customers test all right so we'll go ahead and rename that let's get rid of all of this right here and let's start a new test all right so what would be a typical test that we actually run in our application well how about this if I click on customer list I want to make sure that if I'm not logged in that we are redirected to the login page so let's go ahead and work through this to see how we can make that work so let's write a new test for only logged in users can see the customers list how about that so that is one test that we need to make sure we need to make sure that if somebody tries to fetch that page we need to make sure that they are redirected so we'll say response equals and we'll say this there is a helper built into this test case that we can use and we're gonna say go ahead and get the following route so what is the route that we are looking for well this route right here is just slash customers so we're gonna go to slash customers and then I want to assert a redirect and where are we redirecting to login it's that simple we're gonna fetch this URL and then I want to assert that we actually end up in logged in all right let's try that and see how it goes let's copy this run back here and then run vendor bin PHP unit - - filter and then paste our method name and there we go we have one test and it's passing great so now let's change this code so we can see that fail so let's go back here and see how we can do that well simple enough if we just disabled this middleware then it is no longer required for that so I'm gonna disable that let's go back to the test and run it again and we fail okay and why did we fail we failed because the response status code of 200 which is obviously just a correct response is not a redirect status code great so that's it that is our first test how exciting is that so let's go back here let's turn on authentication again and then run our test one more time and sure enough we are back to green now I like to run clear right before every test like so that way every time I run my suite we get a nice clean screen so from here now that's just what I'm running right before all right let's keep going with this test let's test some more stuff so now that we are testing that if you are not logged in you cannot see that page let's do the inverse let's make sure that if we are logged in then we can view that list great so let's write a new test and say authenticated users can see the customers list so how do we do that well there is a nice helper in level and we can say this acting s so we're going to be acting as a particular you this brings in an entirely new concept of testing so basically what you would want to do is you would normally not want to use the same database that you use for your actual development for testing so what you can actually do is set up an in-memory database instead but this database is gonna be totally fresh every single test so every time we run a test after the test is done it will erase the database and then when the next test starts you have to start over so it actually has this refresh database straight here that we are currently not using and the reason for this trait is that same thing that I just explained is that we need to refresh the database after every single test think of it as if you're gonna perform surgery so of course you want a clean slate and you want every single tool cleaned out and left completely sanitized so that's how it is we want a clean state of our application before every single test so after every test we need to tear everything down and start over in the next test so let's set that up now let's go back to this PHP unit that XML file we can actually use an in-memory SQLite obviously we've been using SQLite for our project but we can set up an in-memory one instead that way is not using our actual file so in this PHP unit file we did disable telescope on here but if you noticed what these are these are environment variables anything in your dot EMV file can be overwritten just for your tests so in my case this is what I'm interested in this DB connection so I'm gonna override DB connection just to make sure that it is always SQLite then on top of that I want to make sure that the database that we are using is in memory so we can say DB database and then to put an in memory you put colon memory : and now that's it so every single test is going to use these variables right here so whenever you need to set a particular configuration for testing only this is where you add them to the PHP unit that XML file alright let's head back here to the test and who are we gonna be acting as well technically we don't have any users our database is empty but if you remember we talked about model factories already we are able to generate any user that we want using a factory and that's the real reason for factories is it actually for testing so let's go ahead and whip up a user right now let's say factory give me a new user and go ahead and persist it to the database now I'm gonna run into an error but I'm gonna continue to write the test so now we are technically logged in right because we are acting as one of the users in our application so let's go ahead and copy this and this time when we visit slash customers because we are logged in I don't expect a redirect anymore but rather I expect the status to be 200 so we can just say assert ok make sure that everything went ok all right we're gonna run into an error right here but let me go ahead and run this test so go ahead and run vendor bin PHP unit and then your method name and there it is so this is the error that I was talking about now we have no such table users if you recall we talked about this already we need to use this refresh database straight so whenever you get this error remember there is a completely fresh database in our system when the test is running so we need to migrate our database so to migrate our database all we need to do is just use this refreshed rate so let's say use refresh data based rate just like that just that command right after your curly braket and let's run that test again and now we get a green so now we know that only logged in users can see that page and we also know that if you are authenticated then you do get to see that page so what else can we test let's go back to this controller and let's see what else we can put under test what about this this store method so if all of the correct data is sent to our store method we you expect a new customer in our database so let's see how we can achieve that let's go to the web routes PHP file and let's check out this customers right here so as we see here when we hit slash customers as a post request we do hit that store method great so let's go back to our test and let's add a new test for a customer can be added through the form okay so do we need to be acting as somebody yes we do we need to make sure that we are logged in so we'll add that in at the top and then we'll say response equals this remember this time it's not a get request but it's actually a post request so we're gonna be hitting the slash customers and then we need to pass in an array of data so as a second parameter we can pass in an array of data so what data is required well if we go to the validate request we see that we need a name email active and a company ID so let's fill those out really quick let's say a name test user an email we'll just say test at test com active we'll set that equal to one and then we need a company ID we'll also set that equal to one we actually don't have a company in our database at this point but we'll just do that as a one all right so then we could say this assert count so let's grab it a customer and grab all of our customers in our database and I want to assert that there is a count of one when we fetch all of the customers in our database now customer as a class is getting imported up here at the top as use app customer so just to make sure if you get a customer was not found make sure you import that class so let's go ahead and try to run this test and see what happens all right so we get an error we fail asserting that the actual size of zero matches the expected size of one so obviously something went wrong with our application and we were unable to do that so this cryptic message here is not going to help us at all and the reason for that is that level behind the scenes is actually doing something called exception handling so it's actually kind of covering up the error for us so let's go back to phpstorm and let's turn that off so we can say this without exception handling it's just a method call and if we add this line right here now lera will no longer handle those for us and I think our error is gonna change let's try it again there we go alright so now we've got a much different error and the error that we were getting is this action is unauthorized of course we did not authorize this user to perform that action all right now if you remember in our customer policy in order for you to create your email has to be admin at admin comm those are the only people allowed to do that right and so in our controller right here right in the store method the very first line is we authorize that request but this is failing because our user right now that we are generating whoever this acting as user is is getting a random email so we can actually override any of the fields inside the factory using the create method inside the create method we can pass an array and override any of the fields and then let's go ahead and give them admin at admin.com so what that's going to do is the factory is gonna generate everything except this email then it's gonna use whatever we passed into the email instead alright let's try that again and see if we get any errors so that worked however we get these messages here and if you remember that's because we're actually firing an event when everything ran it actually ran through our event and it did fire this event but in testing we may not want to fire an event so how could we tell there well go ahead and forget about any events so in testing there's something called fakes which means that you could basically replace one class with a different class so it's basically a dummy class instead so we can actually get rid of this without exception handling because we know in our test is actually running so what we can do is we can call the event facade and that's going to be illuminate support facades and then event and then we can call fake this is a built-in feature of laravel that allows us to fake any events and if you're looking up here at the top that is imported so make sure that you are inputting the correct facade all right so let's run that test again notice how it took ten seconds that's a long time for a single test let's run this again and see what happens there we go so much much faster 400 milliseconds and we did not fire that event so far we've ran one test at a time but if you want to run the entire file then of course you could just say customers test just copy the class name instead so let's run that and instead of passing in a particular method let's go ahead and pass in a class name and there we go so now we have three tests which we know that and we were served four things so figure that you are refactoring your code and all of a sudden you say you know what I don't need this authorization here anymore and you delete that out of your controller entirely when you run your tests boom there it is that is sort of the premise of testing if you're able to delete some code and it doesn't break your tests it means that you're not covering that so that's important that's a good test for you to know that's when you realize well I really do need that line of code because obviously under test that is necessary all right let's just do one more test as this lesson is getting kind of long so let's do one more how about this how could we maybe validate that this validation is happening so that would be a cool test I want to make sure that each of these fields is validated so if tomorrow I say well do I really need this and I erase it I realize that yes I do in fact need that so let's do a quick validation test so let's say something here what I'll actually do is take this array I'm going to need to use it over and over think of this array right now as sort of valid data 100% valid so let's go ahead and just put that out in its own method and I'm just gonna call it data let's make a private function and call it data and it's just gonna return that array right nothing crazy so I make a change let's go back to the test let's run them again yep we're still green all right let's keep going so that refactor was successful so now let's write a new test and let's just say test a name is required we're gonna need all of this again so let me grab everything and let's go and start to modify this so obviously we're gonna be acting still acting as this person but now instead of just this data what I could do is I could say array merge go ahead and array much what you get from this data but I want to override the name and just have an empty string instead so let's see how we can test this we can actually use this response right here to make sure that we have an error so let's say response assert it has an error for name how about that so the name needs to have an error let's go ahead and run this and sure enough it does pass but of course we want to make sure that we actually have zero customers and the reason we want to certain that we have zero customers is because if a name was not provided then of course I don't expect the database to have a customer so that makes total sense let's go ahead and run that test and we're back to green all right so that one is passing how about this let's make sure if you remember the next rule was that it needs to be at least three characters let's put that under test so a name is at least three characters so now I'm gonna override the name and just pass in a just a single character so I still expect that to fail so let's do that now let's run that test so we run our test and we still get green of course because that is still required all right so now let me see that fail what if I removed these two rules all together let me go back here run the test yep we have two failures so we expected the name to be required and it wasn't that failed and then a name is at least three characters also failed so then let's add required back in and go back here and now we have one test failed a name is at least three characters failed because of course it needs to be a minimum of three characters and now we're back to green great now this up here right this is kind of becoming a little bit cumbersome as you can tell we're basically doing the same exact thing every single time so why don't we extract that somewhere else how about this let's start with this event fake so at the top of my test I can actually override the setup method so if we override the protected function setup and it does require this return type then we can maybe put that event fake up here instead so let's do event : : fake and now we can remove that from each individual test because what happens is that the set up method gets run before every single test so any data or anything that you need to do before you run a test that's where it would go there is a set up method and there's also a teardown method so if you need anything after you finish your test that's where it would go so now that we have that up here that cleans up our tests a little bit so now let me go ahead and extract this as well let's extract this to a new method of this just call it acting as admin so acting as admin I'm gonna do the full refactor go ahead and replace go ahead and replace there we go so phpstorm makes that a breeze so now instead of having all that complicated code all I'm saying is this acting as an admin acting as an admin and acting as an admin and now we have a new method up here for acting as an admin let's clean this up let's get rid of that and let's actually make it private so that way nobody can really access this this is not really a concern I'm actually going to move it down here now let's run the test again we did a big refactor but we have our test to back everything up and say you know what everything is still running good great let's keep going let's do that email so now an email we could say an email is required all right so acting as an admin I'm gonna go ahead and send a blank email field and now expect to see an error for email and I expect my database did not have any records all right let me run the test and we pass great now I also expect that to be a valid email I repeated the test let's go ahead and change it to a valid email is required so now let's just put here some text test test test and let's run the test and yep we still pass so if we were to take off for example the email validation and we run our test and of course it fails because that was not required and same thing if we take off the requirement then we have two failures great so we're doing great let's get back to green so there we go so that guys that is a very basic example of testing and how to get it controllers under test so there is a lot more to testing and we're gonna continue to explore testing in a future course but for now just know the basics of PHP unit and what testing does and once you get into the rhythm of testing I promise you you'll never want to write a line of code ever again without writing a test first so with that that's it for today's lesson I'll catch you on the next one
Info
Channel: Coder's Tape
Views: 50,359
Rating: undefined out of 5
Keywords: phpunit, laravel phpunit testing, phpunit testing, phpunit laravel, laravel testing controllers, laravel feature testing, feature testing laravel, unit testing, laravel unit testing controllers, unit testing laravel, laravel 5.8, learn laravel, learn laravel framework step by step, laravel e-commerce, laravel testing, laravel testing tutorial, laravel phpunit, laravel tdd, laravel tdd tutorial, laravel feature test, laravel test workflow, laravel test driven development
Id: RJ_iXzdSpT0
Channel Id: undefined
Length: 26min 5sec (1565 seconds)
Published: Wed May 01 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.