In this video, I’ll share some of my expansions to the 'checkForShip' test suite.
- We might have to adjust our functions as we go
- It’s ok to throw away code that was working, even if you spent time writing tests for it already; it's one of the biggest benefits of having unit tests
- Our unit tests will tell us exactly what breaks, and how, as we rebuild parts of our code
The fire function is one of the last things the game engine actually needs. 0:00 Isn't it amazing how fast we can build stuff that sounded hard before we started? 0:04 So here's how I wrote my test suite for the fire function. 0:08 All right. So first, 0:11 I'll set up my test suite at the bottom of the shiptest.js file. 0:12 The test suite is named fire, so I'll pass fire as the string followed by a function. 0:20 Then I’ll import the fire function at the top. 0:35 So it's available to all my specs. 0:38 I'll skip running the initial test in this case because i know it's just going to 0:56 break at first, but it's a good habit for you to keep. 1:00 All right, so now I need a spec to test fire's behavior. 1:04 The first thing that comes to mind for this function is that it should record 1:07 damage on the correct ship, if there's a hit. 1:11 So I'll create a new spec that says, it should record damage. 1:13 On the given player ship. 1:20 At a given coordinate. 1:27 So now, I need to tell fire which player I'm targeting and 1:40 which location i'm guessing. 1:43 So, it should accept a player parameter and a coordinate parameter. 1:46 That means I need a player object for this test. 1:50 So, I'll say var player. 1:53 And the player will need at least one ship at one location. 2:00 So, I'll say locations. 2:08 0, 0. 2:13 And after the location, I'll add a damage array. 2:17 So if I set up a simple player object and call fire at a location 2:26 I know is occupied, so let's say fire player at 0,0. 2:32 The players only ship should have a matching coordinate and its damage record. 2:40 Basically the ship should take damage at the given location. 2:45 So let's write an expectation for that. 2:49 So right below will say, 2:51 expect (player.ships.damage 2:55 ).to.deep.equal. 3:02 And then will add 00. 3:12 So, if we run the test now in the console, we can see that the test breaks at first. 3:18 So now let's go ahead and set up the fire function in ship_methods.js. 3:31 So right below the damageShip function, I'll create a new function called fire. 3:36 And we'll pass the fire function two parameters, 3:47 player and coordinates. 3:52 Then I'll go ahead and export the fire function at the bottom 3:55 of our file by saying module.exports.fire = fire. 4:00 So now if I go over to the console and run npm test. 4:10 We can see that the test gives me an assertion error. 4:17 It says, expected undefined to deeply equal [ 0, 0 ]. 4:20 Now, the fire function doesn't yet do anything to the target ship. 4:25 But I know that in the end if a ship exist on my opponent's board 4:30 at the location I guessed I want to run damageShip on that particular ship 4:34 using the location I guessed when calling fire. 4:39 So I'll start with check for ship using my guess and 4:46 the given player since this returns a true or false value. 4:50 So inside my function I'll save it to a variable named ship to use it internally. 4:56 Then I'll pass player and coordinates as the arguments. 5:03 So, I don't need to worry at all whether check for ship will work here. 5:09 Because my test suite has already proven that it does exactly what I expect. 5:13 And that's super awesome. 5:18 It takes a huge load off my brain. 5:19 It's kind of like knowing that a j.query function will just work and 5:21 you don't need to second guess it. 5:25 So here, I'm calling the variable ship, because that's what it represents. 5:27 Okay, so if my opponent has a ship where it guessed, then I wanted to take damage, 5:32 so I know which coordinates to pass in, but how do I know which ship to pass? 5:37 Now, I could run through all my opponents' ships and look for the one I want. 5:43 But that's already what checkForShip does. 5:48 And I don't want to repeat all that logic here. 5:51 So instead, let's just go refactor the checkForShip method. 5:53 Remember when I warned you that we might have to adjust our for 5:57 functions, as we go. 6:00 So it's okay to throw away code that was working, 6:02 even if we spent time writing tests for it already. 6:05 In fact, this is one of the biggest benefits of having unit test, red, 6:08 green, refactor. 6:12 Even though our test for checkForShip are passing, it turns out we need check for 6:13 ship to work slightly different now. 6:18 So, our unit tests will tell us exactly what breaks and how as we rebuild things. 6:20 So this is an easy fix. 6:27 I'll just have the checkForShip function return the actual ship. 6:28 And instead of simply returning true when finding a ship. 6:33 So replace true with ship. 6:36 And I've checked for ship find something then I wanna call damaged ship 6:40 on that ship at my guessed location. 6:44 So in my file function I'll say. 6:46 If ship, then damageShip and 6:50 pass the ship and the coordinates. 6:56 All right, so let's see what the test reports. 7:04 In my console, I'll run npm test. 7:07 Well, this is interesting, it looks like all my checkForShip tests are now 7:13 failing because I've dramatically changed the way checkForShip works. 7:18 But it looks like my fire test passes, so that's good. 7:23 So back inside ship_test.js scrolling all the way up to my checkForShip suite. 7:32 I can quickly fix my test up. 7:39 Now, my first test Is fine. 7:41 Because it still returns false when it's finding nothing. 7:43 But i'll change my next specs expectation to.deep.equal. 7:47 Then I'll pass (player.ships). 7:58 All right, let's go over to the console and run the test again. 8:05 And I see that my test is green now for that particular spec, so 8:16 great now I know this strategy works. 8:20 Now moving forward for all tests to pass we'll need to make similar 8:23 deeper quality comparisons in the checkForShips suite, so 8:28 we'll edit some of our other expectations to expect an array instead of troop. 8:32 Now we can simply copy and paste this deep.equal 8:37 expectation across the rest of the checkForShip suite, change some of 8:40 the values to match the first member of an array, and everything should work great. 8:44 All right so I'll copy this deep.equal method, from this expectation. 8:54 Then scroll down to the next spec. 9:00 Then replace the first and second expectation. 9:04 Will leave the values at 0. 9:11 Then scroll down to the next spec and replace all of these as well. 9:13 So we'll change the first one from to be true to deep.equalplayer.ships at 0. 9:20 We'll do the same for the next one. 9:26 And this one will change the value to one and 9:31 will do the same for the next one change that so one, and 9:36 finally we'll replace the fifth one and change this value to two. 9:40 Finally, I'll scroll down to my fire suite. 9:48 Then I'll create one more spec here inside the fire suite 9:51 by copying the first spec and pasting a new spec right below. 9:54 I'll change this new spec's description to should not 10:01 record damage if there is no ship at my coordinates. 10:06 So when I change the target here to [9, 9], 10:16 I would expect the damage array to be empty. 10:21 So let's change the expectation to say, to.be.empty. 10:26 Then remove the second 0 value here after damage, so let's see, 10:32 does fire now correctly leave the ship on damage if we guess wrong? 10:37 Well let's see. 10:41 I'll go over to the console and run npn test. 10:43 And yep, the test already passed. 10:50 Now, I might also want to add some reporting to the fire function so 10:55 that players know whether they hit or not. 10:59 Since this is just a game engine, 11:02 we'd probably have a separate reporting function 11:04 that did fancy stuff with the DOM or printed a useful message in the console. 11:07 For now, this looks really good. 11:12 We have almost all the major logic for the game in place. 11:14 Did you use other assertions in your tests? 11:17 Did you build your fire function differently? 11:19 Share your solutions in the community and compare them to what you see here. 11:22 In this stage, we've learned how to set up our files from Mocha. 11:26 The main file structure of a test suite in Mocha and 11:29 a lot of useful chai assertions to make writing test easy. 11:32 We also did a lot of BDD to outline the basic logic of a battleship game engine. 11:36 It can feel strange, but with practice you'll find that it 11:42 really is super helpful to outline your ideas first. 11:44 We were able to refactor things easily and 11:48 know exactly what changed in our code base. 11:51 Manual testing can never give us that level of confidence. 11:54 And the next stage we'll learn some great features of Mocha for reducing the amount 11:58 of test code we write, while also improving the quality of our test output. 12:01
You need to sign up for Treehouse in order to download course files.Sign up