12. Creating a book


Now that we have tested our authentication and we've looked at some PEST-specific functionality, we should just be able to dive in and start to test something a bit more interesting, and that is going to be creating or storing a book. We're not going to be testing the form, we're going to be building and testing the functionality that's actually going to store
a book for a user, and then later we will go in and build the form. So let's just close everything off here and let's create our new test and just start writing our tests before we do anything at all. So let's go ahead and make a test in here. What I tend to do is call these book and then what's happening, and then of course at the end in the suffix we use test,
and of course we want that to be a PEST test. So over in book store test, let's think about the first thing that we want for this store endpoint. Well, the first thing that comes to mind is we need to be signed in. So let's say only allows authenticated users to post, and we could make this as a higher order test. So let's get rid of this function here,
and let's come down and post through to that endpoint, which we haven't built at the moment, but that's going to be slash books, and then we're just going to say assert status 302. Okay, so that's going to be a redirect. We could add that we're redirected, or we could use one of our helpers that we built earlier, but this is slightly different. We need to be authenticated
rather than be a guest. So we'll refactor this later to make this a little bit clearer rather than having some sort of status. Okay, so let's go and just run our test and just see what we get. We'll specifically run this test. So tests feature and bookstore test, and of course we get a 404 because we haven't built this out yet. So that's the first job to do. Go ahead and build this out.
Let's go make a controller. That's going to be book store controller, and let's go over to our roots and just register this. Let's pull one of these down. Remember, this is posting through to store a book, so that's going to be storing through to books, and we have book store controller. Great. So let's go ahead and run our test again,
and this time if we just come up, we get an error because we haven't added anything that invokes this. So let's go ahead and add our invoke magic method into here, and let's just do nothing, and let's run our test again, and we get a 200. So obviously this is failing because we need to be authenticated or we should
be authenticated redirected. If we're not authenticated, we're not in this case because we don't have any middleware. So let's define this out within the controller, and we need to be signed in. So we'll just use the standard auth middleware. Run that again, and we get green. So we know now to access this, we need to be authenticated.
Now we can write a more interesting test if we just head over to our test here, and that's going to be that it stores a book, and we can figure out what kind of data we want to pass down at this point, and then we can implement it in the back end. So let's say it creates a book. That kind of makes sense. We'll make this closure based because to make this higher order based is not
going to look that good, and we want to go ahead and act as a user. So let's say acting as. Let's go ahead and create a user. So user factory and create, and we want to post through to slash books, and the data that we want to post through is the title, the author, and of course, the status as well that we saw a little bit earlier from the introduction. So let's say title
a book. Let's pull this down, and author is going to be an author, and then we want a status in here. Now this status is going to come from a pre-filled drop down, so it's going to look something like this. Want underscore, to underscore, and read. So we'll implement that later. Now what we want to do is implement this with a pivot table. The reason being is if we create a book, so book one, book one
might be just a book on its own, not attached to a user with no status, but then the pivot table for this will link the user to this book, and that means that later on if we want to, we can reuse books. So rather than each user creating every single book and duplicating books, we might want to store them in one central place and just link them with a pivot table. So that's what we're
looking for. We're looking for the book existing within the books table, and the pivot existing, or the status existing with a pivot inside of some sort of book user table. So let's go ahead and create a checkout for this. So assert database has, the first one is pretty straightforward, that's just in books. So we want to make sure that in the books table we have all this information.
So just these two pieces of information here, and then we also want to assert that in say book underscore user, we have the user's id. So in actual fact let's go and pull this up here and we'll be shifting this around a little bit when we look at our hooks within our tests. So user, and let's say user id, user id, and then the status should be want to read, like so. So that
should then prove to us that this is linked up to a user. We can actually chain this, so why don't we chain this down here, like that, great. Okay, so let's run this test. Obviously this is going to completely fail, and let's have a look why, because there is no table users at the moment. That's not the reason it does fail though. So let's head over to one of our other controllers,
or one of our other tests, and let's just grab our refresh database tray. We'll pull all this in actually. So let's come over to this test and let's get rid of these two for now. Let's run this, and okay, so what is happening? We don't have a books table. That's what we need to do first. So let's go ahead and create our model for this. So make model and book migration
and factory for this, of course because we're testing a factory is going to be really helpful. So let's first of all fill in the migration for the books. Let's go down, very very simple, we just want a title and an author. So let's add both of these in here, author, and that should be enough for now. So let's go ahead and run phpr to migrate, and that's done. So now our test is
going to be slightly more complete. So if we just go over to run this test again, the problem now is that we don't have this in the database table. So now what we can do is start to hook up the relationship between the user. So let's go down to here and say that a user has many books, but this is not going to be attached directly to the books. We need a pivot table. So we've got the
relationship in there, but we don't know how to implement this just yet. So let's go ahead and create out the pivot table. So let's make a migration, create book user, book being first just because it's in alphabetical order, table, and let's go and open that up, create book user table. So for this, we're just going to link up a book with a foreign ID, so book ID,
and we'll constrain that to a user. So let's say user ID. There we go. Let's run phpr to migrate, and we're done. So now we can hook the relationship up, and we should see that information in the database once we hook everything up. In fact, we could make this test half pass by just creating the book and then going ahead and hooking it up later.
So why don't we make half of this pass first of all before we implement that relationship. So we're just going to say book create, really, really simple, and we need to get this information from the request. So let's do that now, and we'll just say request only title and author, done, and let's put our request object in here, not from Guzzle, from Illuminate and HTTP.
Let's just rename this real quick, and rename that to just the normal request. Great. And that should be enough. So that should be enough to pass half of it. Let's rerun our test again, and if we just come up here, we've got books, matches. Ah, so it looks like this isn't actually creating the book. That might be because over in our book model, we don't have our fillable
properties in here. So let's add that in, and that's going to be title and author. Let's run that test again, and we should get something different now. So it's still not in there. So let's figure out why that is, and that is because I've implemented the creation of the book inside of the books relationship within the user model. You probably noticed that.
That is why we test. So let's go over to our bookstore controller, and actually do this in here, and put our request into here, and we should be good. Let's wake up. There we go. So this should now half pass, and it does. So the only issue now is we're not linking this to a user. The book's being created. It's just not being linked to the user. So that is where we
actually now want to come over to the user relationship, and we want to go ahead and implement the books relationship here. So this is just a belongs to many relationship. So we're going to say belongs to many and book, and we're going to need to come back to this in a minute for the pivot status, but let's go over to our bookstore controller and actually attach this to
the user. So we're going to use the currently authenticated user just from the request. Access the books relationship and attach that book, and once we've attached that book, we want to pass in some additional pivot information, which is the status of the book. So we can just pass this in from request and status. So that should be enough on its own to fill that book
user table with the data that we're expecting. If we run our test though, you can see that this doesn't quite work. We've got the user ID in there, or we should have the user ID in there, but the status is not going to be in there. So I don't know what's going on with our test, but let's just open up this and get rid of and want to read because that's not going to work.
Let's just rerun our test again. You can see it's not quite worked. So the reason for this is we don't have, if we just come over to our bookstore controller or our user model, we don't have the pivot information on here. So let's say with pivot and status, and let's also add with timestamps as well. So we get them filled for us. So let's run our test now and it still doesn't work. So let's
go back over to our bookstore controller and see what we are doing here. So let's just have a look. It doesn't look like that is being inserted. I actually have a feeling that we forgot to include the status and we did. Let's go ahead and add that in here now. And let's just roll back our migrations. So php artisan migrate rollback and let's
rerun our migrations just to make sure that's in there. Okay. And there, there it is. So let's try and rerun that test and we get green. Great. So it was just that we'd forgotten the column classic. So after we've done that, we want to go ahead and redirect the user back to maybe just the homepage for now. And that is now storing a book alongside a status. Now we can't forget to test
some validation because of course we require the title. We require the author and we require the status as well. So let's just create out a test down here just to validate that information. Of course, we haven't implemented validation just yet. So let's say it requires a book title, author and status. And let's do this as a higher order test. I think that would make sense.
So let's go and just tap into here because we're going to need to act as that user. Let's create a short closure in here. Let's say this acting as now we want to reuse probably the same user to make this a little bit easier. Let's look at a pet pest specific syntax to run something before each test. Normally within PHP unit in a standard test, you would implement some sort of setup
function or something like that. What we're going to do though is take this user and we're going to say before each and we can run something in here before each test. And what we can actually do is we can say this user. So let's change this to this user. And now the user property is going to be available in every single one of our tests. So this really helps just so you don't have to
scaffold this up in every single test. So we can just say this user in here. And then now we don't need to create that user in here. We can just reference that user again. And that makes our higher order test a little bit cleaner as well. So we're going to go ahead and post through to slash books. And we're just going to assert that the session has errors. And that's going to be
title, author, and status. So we're not specifically validating the status just yet. We will be soon just making sure these are required. Now I'm going to go ahead and get rid of this test just for now, just so we can make sure what we've done here, for example, here, I haven't referenced this user ID is working. Now a really good way of doing this
within pest normally what we would do is we would comment this out a little bit inconvenient. What we can actually do is use a skip method within pest. This is really handy. So we don't have to comment things out, delete them, move them somewhere else if we just want to skip those tests. So let's skip that test and rerun this. And yeah, we've got a failure session is missing
expected key errors. And that is not actually skipping that. So let's figure out why. Let's maybe put this here. And let's finish this off here. And let's just rerun this test. And there we go. So that test was skipped. The reason that didn't work is because we're trying to call skip on the response that we get back. So if you had a closure based like this, rather than a higher
order test, you'd want the skip at the end here. So you might want to skip that test. Otherwise, you'd want it directly on here. So we've just looked at basically skipping test really handy. So we don't have to keep commenting things out. OK, so we know that they're now working out. We switched to that before each. Let's run this again. And of course, it's saying session is
missing the expected errors because we don't have any validation inside of here. So let's validate this just directly within the controller. And then once we've got a passing test, we could potentially move this over to a form request. So let's go ahead and validate our request. The title here, we're just going to say is required. We're going to keep this validation
really basic for now. The author is required. And the status is required. Of course, we're going to want to validate that the status exists as the status is that we want. We don't just want any status put in there. So that should be enough to make that test pass, which it does. And then we can start to add more validation rules in here as well. So in the next part, what we're going to
do is just add another test, which is going to validate the status here. We're going to set this up in a way that we can reuse the statuses across our app using a custom class for our pivot. And then we'll come back to this and just make sure that we're validating that the status that we pass through is an acceptable status.
35 episodes4 hrs 19 mins


Pest is a PHP testing framework that brings beautifully simple syntax to your tests, without sacrificing on features. In this course, we'll get up and running with Pest in a Laravel project and write tests for a real-world application that we'll build along the way.

You'll learn how to set Pest up in a Laravel project, write tests with Pest's built-in assertions, generate code coverage, and more.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!


No comments, yet. Be the first to leave a comment.