hello and welcome everybody my name is Victor and in this series we're gonna be covering test-driven development with Lenovo now I've decided to go with the

most realistic world case that I could give you for this project and what I mean by that is I really want to show you every step in my workflow that I use every day now is my workflow going to

work for you honestly that's up to you to determine only you can know what's the workflow that works for you now this is the workflow that works for

me but it also may not be the workflow that works for me next week I am constantly changing and evolving and looking for better ways to do what I do every day but I do want to show you

what I currently do and hopefully you can learn a lot from it and adapt it into a workflow that works for you your workflow is gonna change you're gonna try out new things those new things

might work and they may not work and you may revert back to your old ways but just keep in mind it's always evolving and it's always changing so don't be afraid to try new things and if they

don't work just revert back to what you were doing before no big deal for the project what are we working on in this series so I've been approached by the local library and they're looking for us

to write a piece of software to manage the library so the library of course has books these books are categorized and of course they have assets and they have authors and they have due dates and they

have reservations and the library also has events that they host and book clubs and all sorts of things so we're gonna be tackling this as real world as I can make it for you from scratch and it's

all going to be test driven so we're gonna write a really strong test suite for this whole entire project and I will try to make it as true to real-world development as I can we're gonna be

doing get commits we're gonna be pushing to github we're gonna be doing everything that I normally do in my workflow and my hope is that through this series you're gonna gain the

confidence to approach projects and really hit the ground running using level as your framework so what's the first thing we need to do well of course we need to make a new project so I will

call mine just library library is just gonna be our working name I don't truthfully have a special name for it just yet but I may come up with a cool name for the whole entire

project but for now library will do all right let's let it all do its thing and we'll be right back all right and we are back let's CD into that project and the very first thing you're

ever gonna do in a project is you need to start your git repository so we'll do get in it now one thing and I don't know if you know this but with level there actually is a lot of get ignore files

built right into the project as a matter of fact if we see what files we have right away we see that we have this get ignore we have git attributes and of course our get project that we just

created so level out-of-the-box is kind of set up to do get now if you're not familiar with git or you haven't used it much it is really a way for us to keep track of

our code and put it into source control now in modern PHP development get is essential so it is very important that you get used to the idea of version controls and commits and pushing to

repositories and branches and everything that is associated with some sort of source control so of course what we've done with that getting it command is simply just create a blank repository if

we run get status then of course we can see that all of our files are here but they are currently on tracked so we need to add those all in and we can do that using git add and then a period which

will add everything and then let's do our first commit so get commit - M and I always call it initial commit pretty common I feel like everybody kind of does it this way all right so now if we

run get status of course we are clean we are good to go all right what else do we need to do well this is gonna be test-driven so really we're gonna dive right into tests right away we're not

gonna worry about the front end of this whole entire project until much much later I want to get the functionality first and once we have all that then we'll worry about the front end so

there's not gonna be any front end in the first half of this entire series so let's go ahead and open this up in phpstorm now of course the tendency for everybody when they start a blank

project is to build the front end but realistically unless you were with the team and you have a designer that's already really focused around the design this is kind of a waste of time

because it will look pretty but it really won't function it won't have any functionality it won't do anything and so you are left with basically something that you spent so much time with and you

can't really ship and you can't really deploy you can't do anything with it so this is why I feel like this approach is better for either a small team or solo developer because every minute that you

spend in the first half of the project is gonna be working towards the goal of finishing the project as opposed to things like design which really will evolve over time however for our project

a library management software is always going to need to do the same things it may look different but the functionality is the same so that much won't change as much as the front-end so that's why this

is important so if we open up library I am gonna be using SQLite for this so let's go ahead and set that up right now and I will do a quick SQLite this is all stuff we've covered in the level basics

course so if you have any questions on that go ahead and check that out so in this course we're really gonna be focused around the test-driven part of it so not much on the basics here all

right so now of course we need to go back and we need to hit touch database database that SQLite all right some more setup stuff let's go into the PHP unit XML file and let's go ahead and set up

an in-memory database so we'll say DB connection is gonna be SQLite and DB database is gonna be : memory : so what we've done with these two is of course we've set up a nice and quick

database environment for our tests so of course we're going to need that so what else what is next let's jump right to it let's go to my test directory let's go into features test an example test all

right so what is the most basic thing that exists in a library well of course I feel like the integral part of a library is a book so why don't we work around the fact that we need to be able

to add books we need to be able to edit and update and do a lot of things related to so let's start right there with that process let me go ahead and rename this

file and let's name it book reservation test now we may decide to change this later on but for now I feel like book reservation test is good enough and describes what we're going to

be doing in here so with that let's go ahead and start our very first test so in order for us to build this project of course we need some sort of way of adding books to our library so maybe

let's start with a simple test of a book can be added to the library now obviously none of this exists and the way test-driven development works is that basically you write code that

doesn't exist right and you have this optimistic way of thinking where you think about how you want to interact with your code and then you let the test drive the development so we're gonna be

doing a lot of back-and-forth with this and at first it may seem like it's daunting because we're taking very very tiny steps but I promise you this is the way to do modern development so stick

with it and I promise you that it's gonna change the way that you develop back to the test a book can be added to the library so let's say this if I hit an endpoint let's just call it books so

if I hit the endpoint books with maybe a title of cool book title and perhaps maybe an author I'm gonna be the author of this one after that hits then I really expect our database to have a

record for a book so maybe let's be optimistic about this and say this assert that the count of my books is equal to one again none of this exists yet we're writing code that doesn't

exist so I definitely want to assert that we have a count of 1 which means that my book got added but I also do want to assert that we got a successful response so let me go ahead and do that

now and let's say a response assert okay all right so we definitely wanna make sure that we got an okay response out of that one I think for now this is good enough

all right so if we go in here I do have this alias of PF and all that does is it just phpunit - - filter now it does clear the screen before but that's not necessary

that's just so that it cleans up my screen so if I grab that test name let me go back here and I'm gonna run PF and then that again PF just stands for this it's just an alias that I have so if I

run that then we get our first failure so our first failure is that we got a 404 a 404 error just means not found and it does not match the expected 200 so though I really do know that this just

means that our route doesn't exist this is not a great example of what a test error should look like because you can think of this as sort of laravel covering the real error this is not the

real error this is the result so to see the real error we can add a new line here and it's called without exception handling so think of this laravel has this exception handling protocol where

when an exception occurs it bubbles up through laravel and then it's translated to a proper HTTP response and that is great because that's what's basically going to give you a 404 page through the

browser but when it comes to testing when you really just want the underlying error we want the real exception so now with that line if we run that we get a much different error and the error that

we get is much more specific we get a not found HTTP exception basically all this means is that there is no post route with slash books which it makes sense we have not even written that yet

so let's go back here and go to my web route and let me erase that route I don't need it so route colon colon post and then what do we know the route is gonna be we know it's gonna be slash

books now we do need a controller for this so let's again do a wishful programming and let's associate this to a books controller and this would really be the store method right because if we

are storing a book then of course we're gonna hit the store method alright let's go back here and run that test again all right so we get another error and it says class app HTTP controller books can

solar does not exist of course we have not made that controller yet so let's go PHP artisan make controller books controller and let's run the test again alright let's see what the error is now

scroll back up so it says that the method store does not exist all right back to phpstorm books controller let's add a new method here store all right let's do it again so now we get that

this book does not exist and what it's referring to is of course I wrote this book right here this class and that's supposed to represent a model in our database but that doesn't exist let's go

ahead and create that now so PHP artisan make me a model by the name of book now I am gonna do a migration for that because I obviously need a migration for a book so let me go ahead and create

that now and let's run the test again alright so it's still getting that book is not found because of course we need to import it yes we create it book right it's over here but book is not inputted

so a couple of options here you can do app book because that's where it's gonna be and in my case I will actually just import the class up here at the top I just think it looks much cleaner alright

let's do it again and of course we've moved on to the next error and the next there is there is no search table books in the database so this is due to the fact that we are not migrating the

database the refresh database straight and you see here it's greyed out because we're actually not using it what it does is every time a test runs and by a test I mean a single test not the whole class

but every time one of these functions runs right before it it actually migrates the database and right after it's done with the test it tears down the database that way you have a nice

clean slate before every single test think of test as a very precise way of you testing your application so of course you don't want a mud database that could cause the test to

fail for a reason that is not what you're testing you really want as clean as slate as you could possibly get so with that let's go ahead and use refresh database and hopefully that'll change

our error run the test again and now we get a failed ascertain that the actual size of zero matches one it's actually failing down here so of course there is no book

there is no count of one so we really do need to have that book put in there so let's go to our books controller and let's do something here let's just blindly grab my book right and I want to

create a new entry and I know that in here eventually we're gonna need validation and everything but let's just say the title is going to be equal to request title and the same thing for the

author all right let's run the test and see if we can get that created alright so we changed our error that means that we've making progress so now it says add title to fillable

properties now we've talked about fillable properties versus guarded I don't personally do the fillable properties I actually do the guarded equals empty array approach I just feel

like I am responsible enough to do this but you don't have to do it this way but this is how I do it so again I want to keep this as real as possible so in my book model I'm gonna set the

guarded property to an empty array so what this is doing is turning off the masked assignment protection that ships with level the reason for this is remember anybody in a forum can add any

fields that they want and try to submit those into your application so if you're not careful you could potentially be open to injection into your database so that's what this is protecting you from

however if you do it the way that I do it where you are explicit about each of the fields that you're passing in there are really no reason why you need that protection it's up to you or your team

really if you want to use it or not alright let's go ahead and keep running through this let's see what we get now let's go back here and we get books has no column

named title of course we have a migration but we have not filled it out at all so create books migration let me go ahead and take this off all right so let's add a new string for title and

let's run the test again let's see what we get now all right so we have no column name author okay let's go ahead and do that now go back run the test and we're green so there we go we worked

through every single error and ended up getting green how awesome is that and that's the way test-driven development works you basically run it until you run out of errors and when you

run out of errors then of course it's working it's ready to go so that's a nice way to do it all right so what do I want to tackle next how about this let's do some validation testing so let's say

a title is required of course a title is required for every single book so I will copy this set up up here whoops looks like I misspelled response let me go ahead and fix that now response I'm sure

you saw that all right so now let's go ahead and hit that endpoint but I'm gonna pass it a blank title and then what I expect is I expect my response to have an error it has to have an error

because validation should fail right so we can say error for title so we should expect to see an error for that title all right let's run that test and see what we get phpunit - - filter a title

is required all right we get an error and the error that we get is we get a not null constraint violation on books title so we're actually getting is our database is complaining and the reason

why our database is complaining is because we are trying to do the insert so we are not protecting at all the validation of course is not in place so our code is letting that go straight

through so let's go ahead and fix that now let's go back to books controller and let's go ahead and start validating our data so we'll say data equals request

validate my request and let's add the title field and let's make that required so now I can actually switch out of this array syntax and just pass in data because data is gonna be our sanitized

data all right let's try it out then we get another error let's see what's going on so it says the given data was invalid so of course this is the error that we are looking for however if we go back

here in our test we are disabling exception handling so let's go ahead and enable exception handling again and let's try that test again and we're passing however not really so we are

passing but I'm gonna run the first test now the first test fails so that we broke something and what we actually broke was a constraint violation because when we

ran this right here we didn't add author so if you don't have author in this array then that's not gonna pass through and so author was not filled so let's try that test one more time and there we

go so that one is passing now let me go ahead and test the entire book reservation test and let's see what we get there we go our test suite is passing

again all right let's keep going let's do another test and this one is not gonna be for title but this of course is gonna be for the author alright so we'll say the author this time it will have a

title so we'll go back to cool title and we're not gonna have an author all right let's try that test and we fail we say session is missing the expected key of error meaning that it didn't have any

errors and we know that because we need to put required right here and let's see what happens now now we're back to green so our entire suite is passing great let's keep going what else do we need

let's go back to this test and so ok so now we have a title and so basically we have a way of adding a book into our database awesome alright so how about this what about

the part where we need to update a book how about that functionality a book can be updated okay so if we submit a post request then at this stage obviously we'll have a book right this test up

here proves that we'll have a book at this point I'm gonna go ahead and take this off of here this response let's put it down here because that this is the one that I actually need so now if we

submit a patch request with title equal to new title and let's just keep author the same as a matter of fact I do need to bring an author in so an author is required let me go ahead and bring one

in here so if I do that what do I expect now well how about this this assert equals so I expect my title to be new title and if and we can grab that by just saying book give me your first one

right there's only gonna be one because remember the database gets erased every single time so I expect that to be true now let's go ahead and do this all in one shot and maybe say new author here

that way we could test both of those at the same time so if we grab the book first then we need to grab the title and then we need to grab the author so this is gonna be our update route let me go

ahead and run this test and let's see what we get so it says no test executed of course I forgot to put the annotation this is the annotation up here let's go ahead and paste that in let's try that

again there we go alright and we get kind of a cryptic error let's see what's going on ah of course I forgot to put my route here we're gonna hit books and hit slash books there we go and we're gonna

pass through with that data there we go that's correct now let's go ahead and run this and so we get that we failed asserting that these two strings equal basically new title and cool title do

not equal and you may be wondering how did we even get down here this patch route doesn't even exist again back to that exception handling so let's run this

without exception handling and let's see we can change the air there we go so now we get the patch method is not supported for that route all right there we go that's what we were looking for so

let's go ahead and change that now so let me do this route again and this time it's gonna be a patch and of course we're gonna hit the update on that one run that again and let's see what we get

so update does not exist on the books controller all right let's do that now books controller and let's add a new public function for update run that again now it's failing to assert that

that changed because we're really not doing the update so let's go ahead and do the update now so what I'll need to do is I'll need to copy this and I'll copy this for now we know we're not

gonna do that but I still need to accept what the actual book is that we are changing so let's do that now let's go back here to my test and we cannot hit your slash books but of course we need

to hit slash books slash the ID of the book that got created it's a couple of different ways that we can grab that but probably the easiest way would be if we just grab it right here right after we

add that let's say book first and then down here we could just add book ID right we need the ID of the book so that we know what it is that we are actually doing all right so back here in my books

controller this is gonna fail again let me run this again and of course we're gonna get this patch doesn't exist because now we're trying to fetch books slash one so I'm gonna use route model

binding of course this is one of my favorite features in lerigot so let's go ahead and import app book as we did up here at the top there we go and so now we're gonna have a book so

now that we have that of course we need to go back here and change our route so it's gonna be slash books slash and remember this needs to match the variable that I just made sorry just

book there we go that needs to match exactly what I just did over here so this book needs to match that is not gonna work so alright let's go

ahead and run this and we're back to the same error where the title doesn't match so we can just say book go ahead and update yourself with the data that was passed in run the test and we are green

awesome alright let's run the entire test suite now and see how we're doing copy all right so we're still green perfect so while we're in a green state

I want to do a quick refactor in my controller so let's go back to this controller and we have this validate data right here and what I want to do is of course I want to extract this into

its own method so let's do something let's extract that into a method called validate request all right phpstorm is going to ask me if I want to also replace the second instance of that and

let's go ahead and replace that and this is what we have so we have this new function that returns the validated data we can change it to protect it it doesn't matter

so now data is equal to that but we can really inline this there's no point in us having this in its own thing so let's go ahead and inline that and there we go and let's also inline this one and there

we go so now we have nice clean single line and now let's go back to our test and we're still green so we know that that refactor worked and this is the beauty of tests I cannot stress this

enough how nice it is to refactor without having to worry about messing up your code that's great all right so we got this working our tests are passing let me go ahead and do a get commit to

wrap up this lesson so get status it looks like we have our test that got renamed we changed our PHP unit foul and a routes file and all that I do want to break this up into a couple of different

commits and I also want to talk about this dot idea directory so the dot idea directory is a phpstorm directory and we really don't want to put that into source control so before anything else

let's go ahead and get rid of this directory altogether so to do that if we go into our files let's find that dot get ignore file this file right here so this is a list of files that you won't

get to ignore altogether so we can add they dot idea directory in here and now it will ignore that file check this out notice how it's here right I'm gonna run the same command again get status and

now it's gone so it's as if it doesn't even exist let me pull up the github desktop app because it's easier to see and there we are alright so the first thing that I

want to commit let me go ahead and turn everything off so let's add this get ignore so update get ignore to exclude dot idea directory nice and simple get commit message what's the next thing

that I want to add alright how about this let's add the book let's add the book controller and let's add the migration so this commit adds the book resource let's commit that alright let's

go ahead and commit PHP unit that XML and we'll say add testing database commit now let's go ahead and add the routes and the tests so maybe just adds basic test for book commit and we're

good to go and with that we'll wrap it up for this lesson in the next lesson we'll continue to work through this books reservation test

    carbon laravel feature testing feature testing laravel git git command line git init github laravel laravel 5.8 laravel carbon laravel feature test laravel feature testing laravel phpunit laravel phpunit testing laravel tailwind setup laravel tdd laravel tdd tutorial laravel test driven development laravel test workflow laravel testing laravel testing controllers laravel testing tutorial laravel unit testing controllers learn laravel phpunit phpunit laravel phpunit testing tailwind css tailwindcss 1.0 laravel test driven laravel unit testing unit testing laravel