Scaffolding the Command Test0:00
Writing tests and assertion for console commands just got a whole lot easier. So why don't we set up a test here. We can put it within the console directory. php artisan make:test, and this will be console/make/UserCommandTest.php. Okay, so now we'll see that within the console directory. Let's try it out. So when we call an artisan command, we'll call it make:user. So this is like us saying, well, when the User does php artisan make:user. Okay, well, I'm going to expect the system to ask a question.
So this is like us saying, well, when the php artisan make:user. Okay, well, I'm going to expect the system to ask a question. And we'll say, which or what name should we use? Now as the second argument, you can provide the input. So we'll say John Doe. And then we'll do a couple more here. And we'll say, and the email address. This will be john@example.com. And then finally, and the password. Okay, so notice we're writing out the test before we've even started on the console command.
And then finally, and the password. Okay, so notice we're writing out the test before we've even started on the artisan command. So when we run that, we expect the User to be prompted with three questions. And once we give that, we're going to expect the output about the John Doe User has been created. Finally, let's do one last test. Let's make sure it does exist in the database. So assertDatabaseHas within the users table, a record where the name is John Doe. Yeah, and I think we're all set to go. So we'll say it generates a new User.
Creating the Console Command1:31
Yeah, and I think we're all set to go. So we'll say it generates a new User. Finally, let's clean this up. And we're all set to go. So if I give this a run, of course it fails. The command make user does not exist. Okay, of course it doesn't. Now we can either generate a console command from the terminal, or within my routes directory, I'm going to open up the console page, and we can just do it quickly in here. So let's overwrite the existing one, and we'll say make user persist a new user to the DB.
I'm going to open up the console page, and we can just do it quickly in here. So let's overwrite the existing one, and we'll say make User persist a new User to the DB. Okay, so if I give that another run, we should change the error. And we did. So we did not ask this question. All right, let's do it now. So this ask, what name should we use? And we'll save that to name. Okay, if I give that another run, all right, we've moved on to the next one. Like so, we'll save that to email, run it again, and the password.
Okay, if I give that another run, all right, we've moved on to the next one. Like so, we'll save that to email, run it again, and the password. Now instead of ask, if you want it to be a little more secure, you could say secret. Okay, let's give it another run. Now we've asked everything, but we still haven't yet created the User. So yes, we could provide the output here, like so, and we have to make JohnDoe a variable. And if we run it again, and we've moved on to the next error. So we've asked all the correct questions, we've provided the necessary output, but at no point did we persist the User. Okay, that's the final step.
Persisting the New User3:10
no point did we persist the User. Okay, that's the final step. So we could import User, and I'll just say forceCreate here, and provide the name, email, and the password. All right, let's give it another run, and it passes. Now one last thing we could do if you want, we can assert the exitCode, and this also is new. Assert exitCode, and of course you may know in the console, an exitCode of zero is success. Yeah, a little confusing, but that's how it works. So if we give that a run, oh, it failed, yeah, that's going to be because we've already persisted.
Resetting DB and Hashing3:40
Yeah, a little confusing, but that's how it works. So if we give that a run, oh, it failed, yeah, that's going to be because we've already persisted a User. So in my database, if I give this a refresh, we saved that User. Oh, and we also forgot to encrypt the password, so we'll take care of that as well, or use a custom mutator, something like that. First, let's use php artisan migrate:refresh, like so, and that way we can run this as many times as we want, and we're always going to get green, because we reset the database, or we use a transaction. Next, within the console directory, let's decrypt this, or again, like I said, on your
use a transaction. Next, within the console directory, let's decrypt this, or again, like I said, on your User model, you could have a set password attribute that would take care of that for you. Now, another way we could write this is do it all inline, so we could say name, and then we just do the call there, that would work, email, and the password, and now we can get rid of all of that, and let's see, did it work? No, it didn't work, of course it didn't, and that's because we haven't yet saved the resulting User, so we'll say $user, and then down here, $user name. Okay, run it again, and now we're back to green.
Considering Test Coupling4:56
user, so we'll say user, and then down here, userName. Okay, run it again, and now we're back to green. So that would work as well. Now in closing, it's not a huge deal, but something to consider is you are kind of very closely coupling your console command to the test. So for example, the syntax, the grammar we use when asking this question, shouldn't affect whether or not the test passes. So if I said, in which email address should we use? Well now, that simple change, we know the code still works, but we changed the wording, it's going to blow up, right?
So it's not as big of an issue as in other areas of your system. But nonetheless, it's something to consider. And in this case, it's kind of unavoidable, because even if you did something like this, where you said, okay, well when I call php artisan make:user, it's going to ask some questions, but ultimately I just expect this User. Well of course it's not going to work, right? Because we didn't provide responses to those questions. So in the past, you end up having to use, honestly, some nasty mocks where you say, okay, well when the first question is prompted to the User, respond with this, and if we need another one, well when the next question is provided, then respond with that.
