Refactoring Duplicate Tests0:00
Okay, I wanted to take some time to refactor our tests. We have a lot of unnecessary duplication here, as I'm not really utilizing factories to their full potential. So if we look at one of our test cases, and this is the same for many of our test cases, we are creating everything we need for the Idea manually, for example, a User, a Category, and a Status, and then we're assigning it to the Idea here. So a lot of the times, for example, in this specific test case, we don't even care about that. And we should let the factory handle that automatically, so we don't have to write it here in our test case.
And we should let the factory handle that automatically, so we don't have to write it here in our test case. So if you take a look at our IdeaFactory here, I am just generating a number between 1 and 20 for the user, 1 and 4 for the category, and 1 and 5 for the status. And that's because I wanted seed data for whenever I tested in the browser. So right now I have 20 users, so 1 for this user, or my user, and 19 random ones. And then I manually create four categories, and then create five statuses here. And then I'm making use of the factory to make sure it's within these bounds here. So that's the reason the factory doesn't create new categories or new statuses directly in here.
Adding Factory States1:09
So that's the reason the factory doesn't create new categories or new statuses directly in here. And when I was writing tests, I sort of just tunnel vision and start copying and pasting from previous tests instead of making use of factories. So ideally, I like to have both cases, I still want to be able to generate 100 ideas here and have them within the bounds of these. But I also want to create statuses and categories and users automatically when I don't care about it in the specific test case. So we can make use of factory states for this. So if you scroll down here, there is a section for states, factory states.
So we can make use of factory states for this. So if you scroll down here, there is a section for states, factory states. So let's have a state for each of the cases I mentioned. So let's grab this. And for this case, the only time I want the bounds to be within these numbers is in the Seeder here. So let's make a state for that. Let's call it, let's say existing. So change this. And we can grab this.
So change this. And we can grab this. So the User, the Status and the Category. And we want it to be this. But now in our factory, we can generate a new User, a new Category and a new Status automatically. So we can just say, User::factory(), and the same for the others as well. So let me just copy this. Okay. And let's replace these with the Category.
Okay. And let's replace these with the category. And this for the status. And I'm not sure if I have the factories defined. So let's make sure that they are. So the User should be defined because that comes with Laravel. Sorry, User factory. Okay. Let's check the status. And I did define it.
Seeding and Factory Verification3:06
Let's check the status. And I did define it. Cool. And the category is defined as well. So now in our database, like I said, for this case, we only want the status and the categories to be within these bounds. So we want it to be this existing state that we just created. So we can do existing, create. So I'm going to php artisan migrate --fresh --seed. And then we'll take a look at the database.
So I'm going to migrate fresh --seed. And then we'll take a look at the database. Okay, so we have an error here. Sorry. It should be a property. It should be a method. Okay. So let's try that again. migrate fresh --seed. And we have to import Category in our factory and Status.
Migrate fresh seed. And we have to import Category in our factory and Status. Sorry about that. Let's try that again. Okay. So now it looks like it works. And if you take a look at the database, you'll see that we still have 100 Ideas and the bounds are within what we defined. Okay. So that's good.
Okay. So that's good. Let's verify. If we call the factory normally, it generates what we want. So in this case, we wanted to generate a new User, a new Category, and a new Status when we call it normally. So let's go ahead and php artisan tinker. Let's do Idea. Let me just clear this factory create. And this should create an Idea, but also a User, a Status and a Category.
Let me just clear this factory create. And this should create an Idea, but also a User, a Status and a Category. Okay. So I created that Idea. As you can see, we have 101 now refresh this, you'll see 101. And it should have also created a new Category. And it did right there, a new Status right here and a new User. And there's 21 now. Okay. So that's exactly what we want.
Simplifying Test Setup4:53
Okay. So that's exactly what we want. We have those two cases. And now in our tests, we can get rid of everything that we don't need. So let's go back to this test here, for example. So this one tests if the Livewire component shows up. And we don't care about any of this. So we can get rid of all this. We actually don't need this too. So let's get rid of it.
We actually don't need this too. So let's get rid of it. And this test should still pass, we can get rid of the variable too. So let's try that. And it still passes. So let's go ahead and do a few more here. So this one is the Livewire component correctly receives the votes count. So we need two users. But we don't really care about the category and the status. So we can get rid of that.
But we don't really care about the category and the status. So we can get rid of that. We can get rid of it here as well. And we don't really care about the title and description as well. Actually, we don't care about the User as well. All we care about is that there are two votes on this Idea here. So we can get rid of the User as well. And if you want, you can even inline the factory call in here. But I think it's easier to read if I see it up here. So when I'm reading my tests, I automatically see that we need two users and an Idea.
Or again, if you want, you can just inline it in here. But I like keeping it up here. We don't need the status. Actually, we don't even need the user because we don't care about who created the Idea. Because we're just testing the category filter in this case. So we don't even need this. We need the category. We don't need the title or description. And we can get rid of these as well. So we only care about the category.
test, making sure that we don't have anything that we don't need. So I'll be back when that's done. Okay, I'm back and I've cleaned up every test. So let's go ahead and run our tests. I have this new alias called testp, which is just php artisan test --parallel. So let's run that. And everything is passing. So a few things here. I created this new folder for our filter tests. And it makes sense to group them together.
I created this new folder for our filter tests. And it makes sense to group them together. So I've done that. Just add a new namespace here for the filters tests. Also for this AdminSetStatusTest, I've added a new state here on the UserFactory called admin. So let's check that out. I just added this new one here, and it just adds an email of an admin. So if you take a look at one of the diffs here for our tests, you'll see that we've removed a lot of unnecessary code, which is definitely a good thing for our refactoring.
Reducing Brittle HTML Assertions8:49
But like I said, these are extremely brittle. For example, if I wanted to change the padding, this test will break. So one way to fix this would be to extract this to an actual class. So any CSS changes won't result in the test breaking. For example, I believe this is a badge. If we named it badge, then that would be okay too. But what I'm going to do here is just change the name of the status. So the reason I have this for this particular test, and same for the other two tests above. Actually, first, let me show you the test. So the test is singleIdeaShowsCorrectlyOnShowPage.
Actually, first, let me show you the test. So the test is single Idea shows correctly on the show page. So this is just asserting that, let's go back to our actual app here. And that doesn't work because we have a new set of data here. So let me just hard refresh. So that's asserting that this badge shows correctly. And I can't just assert the name of the status because the name of the status also appears up here. So the easiest fix would be to just change the name of the status when we're creating it up here.
So the easiest fix would be to just change the name of the status when we're creating it up here. So we have it right here. And in this case, it's open. We actually don't need the classes here. So let me remove that. And let's just rename it to something else. So let's say openUnique. And then we can just assert that we see the text open unique instead of that whole block of HTML.
And then we can just assert that we see the text openUnique instead of that whole block of HTML. So let's remove this false. And let's see if this works. And it does. Cool. So we can do the same for the other two above, I believe they're here. So we have open and considering. So let's change that and say, again, we don't need these classes. So let's rename them to openUnique.
So let's change that and say, again, we don't need these classes. So let's rename them to openUnique. And considering unique, okay. And let's assert against that instead. Okay, so this is openUnique. We don't need a second param. And this one's considering unique. And let's test that out. And it does work. So one more feature that I forgot to add when creating Ideas is to automatically vote for
Auto-Vote on Idea Creation10:57
And it does work. So one more feature that I forgot to add when creating ideas is to automatically vote for an idea after you create it. So I want to create an idea. So let's actually do that here. Let's create an idea. Let me log in here. So after I create an idea, I want to automatically vote for it. So let's say new idea, hello there. And then I want it to automatically have one vote and the logged in user should be the
So let's say new Idea, hello there. And then I want it to automatically have one Vote and the logged in User should be the one who voted for it. So let's do that. Actually, let's start with the test. So that should be the createIdea test. And it should be down here. CreateIdea, creating an idea works correctly. And down here, we assert that the idea or the newly created idea is in the ideas table. So we can do the same for the Vote that we expect.
And down here, we assert that the Idea or the newly created Idea is in the ideas table. So we can do the same for the Vote that we expect. So let's say votes, the userId or the ideaId is one, since there's only one, the one we just created. And same for the userId. So let's say userId is one as well. So if we run this, obviously, this is going to fail because we haven't implemented it yet. Okay. So let's go ahead and do that.
Okay. So let's go ahead and do that. So let's go to createIdea. And after we create the Idea, so right here, we can vote for that Idea. So we can do this in two ways, we can use the Vote model directly and create a new entry. So the ideaId is the ideaId. And let's grab that from up here. Okay. And the userId is the logged in user. Okay.
And the userId is the logged in user. Okay. And that's going to be auth()->id(). Okay, so let's see if this works. I think we have to import Vote. And let's run that test. And then we'll see if it works in the browser. Let's run this. And it does pass. Actually, before I run in the browser, a few changes here, instead of wrapping all of the
And it does pass. Actually, before I run in the browser, a few changes here, instead of wrapping all of the code in this auth check, let's just return early if we're a guest. So if auth guest, then abort. So we can grab this, put this in here. And we no longer need to wrap our code in this if statement. Okay, so let's get rid of this. And let's fix the indentation here. Okay, so this should work. Let's run that test again.
Okay, so this should work. Let's run that test again. Still passes. And I believe instead of doing this, we have a method on the Idea model that lets us vote directly for an idea. So where is it? Right here, vote. So we can do idea->vote($user) instead of doing this. So let's see if that works. So we have idea, vote, and the $user voting for this idea is the logged in $user.
So let's see if that works. So we have Idea, Vote, and the User voting for this Idea is the logged in User. Okay, let's see if this works. And then we'll test it in the browser. So save that. Run the test again. Still works. Okay, and let's get rid of this. And let's go ahead and test this in the browser real quick. Let's refresh.
And let's go ahead and test this in the browser real quick. Let's refresh. I'm logged in. Another Idea. Hello there. And this should automatically be voted for. And it is. Cool. So one more time, let's run our entire test suite. We are passing and our tests are much cleaner.
So one more time, let's run our entire test suite. We are passing and our tests are much cleaner. So as always, let's make a commit. This is episode 36. So git add, git commit, episode 36, refactor, tests.
