Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Well done!
You have completed JavaScript Unit Testing!
You have completed JavaScript Unit Testing!
Preview
In this video, Iβll share some of my expansions to the 'checkForShip' test suite.
Resources
Video review
- 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
Related Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign upRelated Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign up
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[0].damage
2:55
[0]).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[0]).
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 upYou need to sign up for Treehouse in order to set up Workspace
Sign up