Intro To JavaScript Unit Testing & BDD (2 Hour+ Course)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hey everyone my name is gil hernandez and i'm an instructor at treehouse at treehouse we offer unlimited access to hundreds of hours of courses workshops coding challenges and learning tracks at our website teamtreehouse.com if you like this style of this course and want to learn more about programming check out the link in the description i'll see you there in this course you'll practice a way to write javascript called behavior driven development or bdd in bdd we write special functions called tests for our application before we actually write any real code these tests describe how our code should work in other words tests describe the expected behavior of our application having these tests written already will help us understand the code we want to write like an outline as we continue writing our code our tests will let us know as soon as our programs behave the way we want them to they'll also tell us when we've accidentally broken something along the way we'll build up our tests to cover one small unit of our code at a time this style of testing is called unit testing since unit tests focus on small meaningful chunks of functionality good unit tests are easy to understand they prove that each piece of our code does what we want in the next video i'll cover some of the ways unit testing makes development easier when developing we're constantly in the process of testing things we write code check that it does what we want and make sure nothing else broke in the meantime as we're starting out we usually just write some code and then run it to see if it breaks for example we might write a function that adds three numbers together how do we know that we're done and that the function works we probably load the function in our terminal or in the browser console and just see what happens even though this tells us whether our code works it doesn't tell us very much about why it fails when it breaks javascript programmers also often add console.log statements to track variables and functions what the this keyword refers to if their math is correct and whether things are happening in the correct order consider this gather names of function which expects an array of people objects with names it should return a new array with just the names of each person have i written it correctly maybe you can already see whether it will work in order to check i might just try running the code here in my browser console to see what happens well i see that it doesn't throw an error right away but i still haven't proven that it works i only know that it's valid javascript next i might try using the function with a quick example so in this case i'll pass an array of people into the function then save the output to the names variable still nothing broke so that's a good sign but again i haven't actually proven that gather names of does what i want because i haven't actually seen the value of names and if i add a console.log you can see that names is not what i expected it to be all their names are undefined and now i still don't know what the problem is even after all this generic manual testing i have more debugging ahead of me to fix this function so this method of checking code is very tedious and in the end i'm still unsure of what went wrong or how to fix it so a number of things makes this process very low value first i was only able to see one specific piece of my code we only have one function in this case but imagine if i had a big application spanning multiple files and interacting with one another i would have to repeat this process across the whole stack to find a problem taking even more of my time and energy i also have to add a lot of code to my real application just to make the tests work now i've loaded up my code here with silly variables and arrays and function calls that i don't really need i just put them in here to see what's happening inside the program that makes my code harder to read and changes the way my program runs and i also have to remember to remove everything i wrote just for testing before i really publish my code anywhere for example var people and var names don't really do anything in my application they're just example tests i used to figure out what's going on and if i forget to remove any of these statements my application might break and cause more headaches also even while i'm debugging i have to remember what all my console.log statements actually mean usually that means i have to spend extra time typing little notes to myself from inside every console.log again we only have one here but if i were manually testing a big application there might be a lot of these log statements illustrating the issue most importantly these are just temporary fixes and they don't increase your ultimate confidence in your code as it continues to grow and change so what happens if i change the way gather names of works later i'll have to go back and add all of these statements again and maybe have to totally rethink what i'm testing and i won't know if my changes affected other parts of my code that depend on gather names of without testing those as well there's a better way we'll still always need this method of guess and check development but we can accomplish a lot of the same things more succinctly using automated tests in the next video i'll show you an example of how automated testing improves our development in all these ways in this course we'll be using a tool called mocha to write automated tests for individual units of code in our application we'll build a collection of test functions called a suite moca like all testing frameworks lets us run all of our tests whenever we want to so we can make sure our functions run as we expect them to this will increase our confidence that our code is working and it will make understanding problems much easier so let me show you how easy it can be developers use the term test suite in different ways sometimes some use it to refer to the whole collection of all our tests sometimes it means a particular selection of tests that are related for example all the tests for logging into our website might compose a test suite now the team that designed mocha uses suite to describe a specific group of related tests so that's how i'll use it too just be aware that some people use it in different ways and you might have to ask someone what exactly they mean depending on the context so i've already written a test suite for our tiny gather names of function we used in the previous video and don't worry about what any of this means yet i'll talk a lot more about how to write a test suite and what it looks like soon for now i just want to show you how mocha runs tests with mocha already installed and my test set up i can go over to the console and simply type mocha followed by test and mocha automatically runs the test to check that the function behaves the way we expect so we can see here an output that gather names of looks good in our first test it returns an array and the array is just as long as the array we pass in but then it fails when we expect it to give us the name back from the object we can see that gather names of returns undefined instead of the person's actual name and when i'm ready to go fix this function mocha tells me that i can check line 23 column 25 of my utility spec dot js file to start debugging so the error mocha is reporting is just helpful enough in this case so it says expected undefined to equal gunter so that means our function is returning undefined at some point instead of gunter so if i go look in the gather names of function in my utilities.js file i see one return statement for my array but the function being used by map doesn't return anything i'm getting undefined in my new array because gather names of checks for a name but never returns it so i'll fix this function by adding return before person.name i'll save this file and when i run the test again by typing mocha tests we can see that they all passed so great now i know that the function is working don't worry if this doesn't make a whole lot of sense just yet it will all make more sense later the main takeaway here is understanding how automated testing solves a lot of the problems with manual testing we noted in the last video first our test suite could cover every important function in our code and when we run the tests we can see all of the results nicely displayed in an informative way so we don't have the problem of our results being too narrow and second the test files are completely separate from our real code so we don't have to make any changes there or clean up our live code later third the output is easy to read and understand so it's much easier to interpret what we're seeing here than in our console.log statements and finally good tests guarantee that our code works the way we expect we can run the tests over and over anytime we make a change anywhere and instantly see whether anything new has broken we'll even see what the specific nature of the problem is as you learned automated tests handle a ton of the hard tedious work we were previously doing by hand it makes our code more reliable it makes programming less frustrating and the benefits scale more and more as our applications grow the kind of automated tests we'll learn to write in this course are called unit tests unit testing involves writing tests that confirm an individual function or piece of code works the way we want it to we write unit tests for code that has a clear specific purpose for example we'll often write unit tests for the individual functions in a program the unit tests can then call the function without needing to run our entire application to make sure we know that the function behaves as we expect unit tests are kind of like a basketball drill as a basketball player you need to master various skills like shooting passing running dribbling and so on basketball players run specific drills to test each of these skills outside of an actual game a basketball player might just practice free throws for a while and then practice dribbling at a different time without worrying about shooting or passing or what their teammates are doing a unit test is like one of these drills and we can run unit tests constantly during our development process to ensure that everything is working every time we make a change but being good at each single thing isn't enough to be a really good basketball player they also have to be able to do them all together in addition to practicing each individual basketball skill players practice drills as a team they need to make sure that they can dribble pass and shoot just as well when there are other people doing their part around them and that they can do them all together in the correct sequence in programming this kind of testing is called integration testing you use integration tests when you add new code to pre-existing code to make sure that not only do all of the pieces work individually as expected but also that they run together correctly without breaking product managers or people who run open source projects might also run integration tests whenever others write code to contribute to their projects to make sure that the code others write doesn't break their existing project that's useful on a basketball team it's not enough that your skills are up to par and that you work well together as a team you also have to play actual games live games are very different even from practice games we play basketball on different courts with different crowds and against teams with different strategies so being a good basketball team requires a lot of team experience in this context in software this kind of testing is called end-to-end testing we fire up the application and run it from start to finish for all the user stories we can think of this ensures that the program is ready to go live and that the special details of deployment don't screw up the stuff that we carefully tested on our local machines with unit test and integration tests we conduct end-to-end tests very occasionally maybe only a few times during a product's life cycle as they're very time consuming and expensive you might conduct some or all of these kind of tests where you work or in your personal projects we'll focus on unit testing in this course because that's what you'll spend most of the time writing unit tests actually help us write better code and grow as developers as soon as we start using them so we're almost ready to dig into writing some unit tests i hope you're excited as i am before we do i want to talk about behavior-driven development or bdd bdd isn't a type of testing like unit testing or integration testing instead bdd is an approach to building software if you've ever written an essay or a story you probably noticed that it's really hard when you don't have a plan you forget what you wanted to say or you don't say quite what you mean even if you spend a lot of time writing so it helps to start your writing process with a brainstorm or an outline so that you can always have an idea where you're headed behavior driven development is like creating a plan before you write your program instead of writing a bunch of code and then seeing if it works like a lot of programmers do with bdd you write tests first and code second now this may seem counterintuitive but bdd makes us start by describing the behavior of parts of a program how should this function behave when something happens what should we expect to get back from this function if we give it a number instead of a string in other words we start by describing how the program should work these are our tests and then we write code until it does work the way we expect it to at first our expectations will not be met because the functions we're testing don't even exist yet so the output of these errors will be bright red and that's okay then step by step we code our function until the tests pass that is until bright red errors are replaced with passing green check marks as we fix the errors one by one we get new errors that give us guidance in writing the next piece of our function at first you don't worry about the code looking good or being efficient you just want to see the tests pass if our tests are good we can be confident that our functions really do what they're supposed to at this point we can now go back to our function and change things around to make it nicer to read or faster to run this process of improving our code is called refactoring now the great part of this process is that once we have our tests in place we'll immediately know when a change to our code breaks our application's functionality a red error indicating a failing test this development cycle is called red green refactor first write the tests even though at first they'll fail they might not even compile correctly at first if they refer to things that don't exist yet second fix the function in the first way that comes to mind just to get our tests passing and third go back and refactor the function for improvements repeating the cycle until we're ready to move on in the next video i'll show you exactly how to do this with some clear examples to show you how bdd and the red green refactor cycle helps us let's imagine we have an application pulling movie titles from different sites on the internet because the information comes from multiple websites we can't be sure that the titles coming in have the style we want we want the titles in our database to be nicely title cased which means that the first letter of every word is capitalized except for certain less important words like the and of and so on i've linked to some examples of title case and the teachers notes as well so let's try writing a function that can handle this title casing for us using bdd principles first let's install a testing tool called chai chai is what's called an expectation library chai includes special functions that throw an error when an expectation is not met for now we can use chai functions to write our bdd outline for a small function you can follow along in workspaces or in your own command line terminal on your computer okay so first we need to install chai for our project so in my workspace console i'll type npm install chai followed by dash dash save dev so i'm using the save dev flag here for my mpm installation instead of just save because chai won't be a dependency for our working code we don't actually need chai or any of our testing tools in order to run our application we just need it for our development process now if you forget how to set this up be sure to check the teacher's notes for a link to chi's documentation and installation instructions next i'll create a new javascript file by going over to the console and typing touch textus.js i'm calling my javascript file textuities.js because the title case function we're going to write is just a tool for manipulating text once again you can refresh your sidebar to see the new text utilities.js file so when i open our text utilities file the first thing i'm going to do is import the stuff we want from the chai library we specifically want to use the expect method check the teachers notes to read about other methods you might prefer but i'm just going to use expect for the rest of this course so to import expect from the chai library i'll type var expect equals dot expect remember to import stuff we write require followed by the name of our library as a string so inside the quotes i'll type chai and now node will know to go look for a package with that name in our node modules directory now you might not have seen the style of require statement before but don't worry since i don't want to use everything in chai i'm just saving the expect method to a variable and throwing the rest away so writing it this way is a more efficient way of writing it like this so varchar equals require chai followed by var expect equals chai.expect now that we have the expect function in our file we can use it to write some expectations as our outline to use it we write expect and pass in a value we want to use for comparison then we chain some special chai methods to tell chai what we expect that value to be so for example we can say expect true dot 2 dot b dot false so expect true to be false now this is obviously going to fail because true does not equal false so if i save this and run our test file in the console using node followed by the name of our file which is text utilities.js once i hit enter we see that i get an error it says assertion error expected true to be false cool so now i can go back to our code and fix this example so that it's passing by switching false with true and now if i run nodetextus.js again we don't see any output at all so there were no errors we're going to see a lot more useful output when we combine chai with our unit testing framework mocha in the next stage but for now this will be our green all right so now let's write our first expectation for the title case function so i'll start by writing expect title case and then pass in an example of a movie title our application might be sent from the internet so i'm going to type the great mouse detective notice the title has no capital letters i'm just making this up as an example for me to test against i could put anything i want to in here this is just acting as a guide for me as i write this function so what do i expect the output of our title case function with the great mouse detective to be well i definitely know that i should get a string back right a string goes in and a string comes out so let's start with that so i'll say dot 2 dot b dot a string so expect title case the great mouse detective to be a string with my first expectation written i could go run our file again in the console with the command node text utilities.js and this gives me an error now it says reference error title case is not defined and that's right because i haven't written the title case function yet now we should fix just one error at a time so let's make this go away back in my text utilities.js file right above the test we just wrote i'll write a new function named title case so i'll say function title case so now when i save the file and run the file again in the console i see that i stop getting a reference error instead i get an error that says assertion error expected undefined to be a string perfect it looks like we're making progress now at this point i know that title case is going to accept a string as an argument and it's going to do some stuff to it and give me back a new string so i can get our current test to pass by doing just that much so if i let the title case function accept a title and then spit it back out by typing return title once i save my javascript file and run the test again in the console great we can see that it meets our expectations but our expectations aren't really accurate yet right and this is an important lesson about testing we can pass all our tests even if nothing works correctly if our test code doesn't really test what we want it to so in the next video let's add some more expectations to flesh out our outline we'll be talking a lot about how to do this during the course for now i've linked to an article in the teachers notes about ways we can make our expectations really informative okay so let's start on a new line and still keep our first expectation so that if anything breaks along the way we'll know why and you can also go ahead and remove the simple test expectation we wrote in the previous video also go ahead and get rid of the comments so now i'll write a new expectation by typing expect title case the great mouse detective now we don't just want the same string back right we want the function to give us a different string so in our example the return string should be the great mouse detective with correct capitalization so let's start there this time i'll write dot 2 dot equal the great mouse detective notice how each word begins with a capital letter now if we save our file and run this in the console by typing node text utilities.js we see that we get an assertion error it says expected the great mouse detective to equal the great mouse detective capitalized of course because right now our title case function just returns the same string we put in so to make this test pass we basically need to make our whole function work now that's a little bit too big and it's not very helpful for this test to just tell us make it work like it's doing now so we should break this up into smaller expectations that we know how to tackle usually when writing tests the most comprehensive expectation should be written last that way the function passes all the simpler tests first so we'll keep this two equal expectation at the bottom of all tests so that we know when we've finished then right above it we'll write a new test so now we should think about the smallest piece of the problem we know how to solve so what if we passed in just one letter that shouldn't be hard right the function would just return the same letter capitalized so our expectation should be something that captures that so we'll say expect lowercase a to equal capital a so now when we save the file go over to the console and run our test again we can see that it doesn't work yet we get the assertion error expected lowercase a to equal capital a okay we can definitely do this because it's built into javascript so back in the text utilities file i can call the to uppercase method on our title so now if i save and run the test again we see that we passed our new smaller tests and we still failed a final test but for a different reason so now we're seeing the assertion error expected the great mouse detective to equal the great mass detective so all caps versus the correct capitalization well this is good progress but now we're capitalizing too much instead of not enough so now let's add another expectation with a bit more complexity so right below the expectation which is stroke say expect title case to equal and this time let's use a single word title but more than one letter so first i'll type the title vertical and all lowercase letter then i'll type vertigo with a capital v running the tests gives us the same problem as before but it helps to keep the problem small and comprehensible by confining our expectation to only one word so instead of uppercasing the entire title let's target just the first character in the title this time when we run the tests we get expected capital v to equal vertigo right because we're only returning the first letter so back in our function let's add the rest of the title in skipping over the first letter so right after two uppercase we'll say plus title.substring and we'll pass one so i'll run the test again in the console and great it looks like we passed our vertigo test so now our final test says assertion error expected the great mouse detective to equal the great mouse detective so the first letter capitalized versus the correct title casing so we're getting closer we know how to capitalize the first letter of a string we know how to add strings together we just need to do this for each word in the title i think we're ready to try making it work we have a pretty thorough understanding of the problem at this point and several tests to keep us on track if we misstep okay so we know what to do with one word if we keep our final test case to mind we know we need to break the string apart into several pieces so that we can do our one word part on each word in the title in the title case function we can start by splitting the title up at the spaces i'll type var words equals title dot split and i'll leave a space inside the quotes so now we have an array of individual words from our title now before we even get stuck figuring out the middle i like to write the return statement because i already pretty much know what it will look like we know we have to give back a string that's been title cased we know that somewhere in this function we have to put all the words we just separated back together so we may as well do that in the return statement so i'll write return title cased words dot join i know i have to join them back together with a space because that's how i took them apart so right now i'm going to just guess that i'll make an array called title case words because i know i have to transform all the individual words from my words array somehow all right so now that i have the start and end of my function a lot is already done for me i know i need to make this array title cased words and its value will be derived from the words array so i can at least type var title cased words equals words now here's the logic part i need to loop through all the words do my title case logic on them and then store them in title cased words so let's see do you already know a function that will do something to each member of an array and give me back an array of the same length yup that's right the map function can do that for us in fact we use this for the gather names of function in the first video so back in my title case word variable i'll add the map function right afterwards next i'll move all my title case logic for one word into the map callback and now i'll get every word title case to my title so in the map callback i'll password then in the return statement i'll change title to word so now when i save my javascript file and run the test again in the console great it looks like everything passes this function isn't quite finished i'll talk more about covering edge cases in the next stage so far we've made a lot of assumptions about the input for this function for example we've always started with a completely lowercase string with no punctuation or weird characters but what happens if we get a title that isn't nicely formatted already like this type we might need to expand our function to account for these things for now try and imagine a simple expectation for the title case function that would fail remember titles don't really capitalize the first letter of every word they skip less important words so you can try fixing the title case function to practice your bdd strategy and get more comfortable writing expectations with chai even if we had been overwhelmed or confused about where to start with our title case function bdd helped us break the problem down into smaller and easier pieces as we work towards passing tests we got a better and better function not only did red green refactor help us write our code but now we actually have some simple tests for our function too it might seem strange at first to just think about these tiny examples and expectations but the secret is that this is one of the easiest ways to program once you get used to writing unit tests your code will also be better because you'll have thought about it much more carefully you'll even have tests for your functions that you can show other people to explain what your code does or get extra help that's pretty cool in the last stage we talked about a style of development called behavior-driven development or bdd it means we write unit tests as a guide or outline before we start writing our application code we practice this strategy using an expectation library called chai in the example code we wrote our expectation functions were included directly in our running code and they didn't have significantly better output than console.log statements as you'll learn in this section of the course the professional approach to writing unit tests is to put your test code in a file that's separate from your applications code i'll show you how to do that the important thing to keep in mind and what separates unit testing from logging to the console is that tests describe expected behaviors unit tests focus on the concrete output of functions without worrying about how the function does it now we could have written our title case function in any way that made sense to us as long as it ultimately produced the end result or behavior we expected in this stage we'll use bdd to build a game engine for a digital version of battleship we'll use a testing framework called mocha to make our bdd approach even better we can keep all of our test functions in a separate file and even set them up to run automatically anytime we make a change so that we'll know right away whether we're improving to get started we need to install mocha go ahead and follow along in workspaces or in your own terminal since we're starting a completely new project i'm going to start in a new workspace now i named this workspace javascript unit testing basics but if you're following along outside of workspaces you can name the project directory on your computer battleship engine or something more meaningful so in order to have mocha and chai in my project i can type npm install followed by the save dev flag then mocha chai i use the save dev flag here because these tools are just for development and once they're installed you can see the new node modules directory that includes the new libraries by simply refreshing the workspace sidebar with mocha now installed we can call mocha followed by the name of a javascript file containing our test from the console and we'll see the test results output there so for example if i create a new file called main underscore test dot js then in the console run mocha main underscore test.js mocha prints zero passing in green to my console because there are no tests in maintess.js to pass or fail but we don't always want to run mocha specifically for each test file we might have a large project might have many test files all around and it would be annoying to run each one individually so in the next video i'll show you how to set up our test files to make this really easy different frameworks require us to set up our file structure differently they want us to name our test files differently or put them in different places the main issue is that we don't want to track down our test files manually every time we run them the whole point of automated testing is that our tests are easier and better to use than our old manual testing style if our tests are annoying to use then we might go through all the trouble of writing them only to never actually run them while we're developing that would be a huge waste so we want our test to be easy to run and write that way we're more likely to actually test our code mocha has a really convenient feature since we've already installed mocha as a dependency for our project we can have npm use it to run all of our tests automatically so first in the console we'll run npm init this sets up our project to be portable and it takes advantage of some convenient mpm features now in the console npm asks us a bunch of questions about our project setup but for now i'm just going to press enter for all of these and accept the defaults since i don't plan to publish this project anywhere the result is that we have a package.json file for our project now it doesn't have much in it right now but notice that the scripts field has a test method already by default the test method will normally just give us a warning in our terminal or console that says error no test specified but in our package.json file npm has already set the test command to use mocha and although we've been explicitly telling mocha which files to run we can actually save even more time by keeping all of our test files in a directory called test it's important that the directory is specifically named test not test or test with a capital t and it has to be located at the same level of our project structure as the package.json file it makes sense to have all our tests in one directory at the top of our project it makes it easy to pull in code from other files in our project because the file path will always be from the same directory it also makes it easy to organize our tests and for ourselves to find the right test file after we see its output in our console now if we forget to install mocha before running npm init we can always just go change this part of our package.json file by hand and you can check the teachers notes to learn more about customizing your package.json file to run tasks for us like starting our automated test script so if we follow these guidelines then we just get to run npm test and moco will automatically run every single test in the test directory for us so let's create a new directory called test here at the root of our project and i'll move the main test.js file inside the new test folder so now when i run npm test in the console i get the same output as running mocha great earlier i mentioned that it's important that the directory is specifically named test in all lower case letters naming the test directory badly is a common mistake developers make when setting up sweeps so let's see what mocha's output is when we use a directory name other than test for instance let's change the name to tests then run npm test in the console so now mocha can't find what it's looking for so we get this test failed error in the console output and the same would happen if the name is test with a capital t so i'll change the name back to test then run npm test in the console and now moca once again automatically runs every single test in the test directory okay so in the next video we'll really start building our first test suite let's open up our maintest.js file and start really using mocha the first thing to do is set up a test suite remember a test suite is a block of unit tests that are all closely related because they test the same function or they test similar parts of our code base we introduce a test suite in mocha using describe describe is a function that takes two arguments a string and another function we use the string to describe what the suite will cover so for example let's write a test for ourselves to prove that mocha is working properly this kind of trivial check is called a sanity check because it's easy to write and it verifies that all our code structure is in proper order before we get started doing this now will help us confirm that things at least worked at some point even if we experience bugs later down the road since we're testing our mocha framework i'll just write mocha as the first argument mocha is the thing we're testing so that's a good title for this suite now the second argument is an anonymous function it doesn't require any arguments it's just a wrapper for all of the individual unit tests that we're going to include within the suite so now with our suite in place we can start adding some simple unit tests each individual unit test is sometimes called a spec and just like our suites mocha makes it natural to write our spec by containing them in a function called it it is similar to describe the first argument is a string that describes the particular behavior this unit test is responsible for and again just as a sanity check for myself i'll say should run our tests using npm the second argument is a function this time the function should contain all of our expectations for this unit remember an expectation gives a specific state condition for a test to count as passing otherwise it throws an error so let's import chai at the top of our file so that we can use it as an expectation here so once we have the expect method we can write an expectation inside of our lonely unit tests so i'll start by writing something i know will pass just to prove that mocha has access to this file here i'll say expect true dot 2 dot b dot ok so expect true to be ok now ok is an assertion method for chai it tests whether some value is truthy that means any value besides undefined not a number false an empty string or zero so any value that would satisfy an if condition okay so now if we run npm test in our console we see some really nice output it says should run our tests using npm well that's easy to read and informative so it looks like everything is ready to go in the next video we'll start writing our bdd outline for battleship let's start building our battleship game engine in battleship there are two grids one for each player each player secretly places their set of five ships on the grid and then players take turns guessing where the ships are located if you correctly guess that a ship is located at a position on your opponent's grid your opponent marks that spot as a hit if a ship has been hit at every unit of its length the ship is sunk the first player to successfully sync all of their opponent ships is the winner [Applause] so we'll need to write code that handles the current player and winner the number of ships their position and their status as active or sunk i know that sounds like a lot but just like our title case function from the end of the previous stage we'll use bdd to break all this down into small chunks by writing unit tests so let's start with the smallest part of the game the ships inside the test directory i'll create a new test file to contain all of the tests about ship features i'll call the file ship underscore test dot js i like adding the underscore test into my test file names so that i can find them more easily later and it's a bit more obvious what my error reports are trying to tell me in the test output okay so let's import expect from chai first so at this point i can already guess at a few functions we might need we'll probably need some kind of function to tell us whether a ship is located at certain coordinates so for example when a player puts a ship on the board or when a player tries to attack we need to check whether a ship is positioned at the location they're naming so let's set up a new suite for this function using describe and since the function is just going to check for a ship i'll name it check for ship remember mocha's representation of a test suite the describe function takes two arguments a string describing all the tests inside and a function to wrap them all together so now let's import the check for ship function here at the top of our describe function because all the tests in the suite will need access to it remember this suite will just break at first because the function we're referring to here doesn't exist at all this is just an outline for now so i'm making an educated guess that will want a special directory named gamelogic to contain all of our game logic functions and that will keep all the ship centered functions in a file of their own so what should check for ship actually do well we'll probably give it a coordinate if there's no ship there i think it should probably just return false we might change that later if our functions work differently than we expect maybe in the end we need it to return more information than just false but for now that makes sense so we'll start there in mocha a spec looks very similar to a sweet it takes two arguments a string describing our desired behavior and a function that wraps all of the specs expectations and logic up together each spec should be responsible for just one aspect of the function's behavior so in order to describe that particular behavior of check for ship i'll name this test spec should correctly report no ship at a given coordinate that's really informative it communicates what the spec is designed to prove and also some useful information about the nature of the function the function takes a coordinate of some kind but i just realized that this isn't enough information because there are two boards in battleship yours and your opponents so check for ship will also need to accept a player so that it knows which board is checking for the given coordinates so i'll change this to should correctly report no ship at a given player's coordinate it's best to keep a tight feedback loop in bdd since i've written a lot of test code already let's run our test and see what happens so in the console i'll run npm test and i see that i get an error it says air cannot find module game logic ship methods well remember the ship methods file isn't even really it so let's go fix that so i'll create a new folder named game underscore logic then inside this new folder i'll create the ship underscore methods.js file i'll go ahead and keep my ship test file open to compare against and just copy over any code i'll need later so i'll start by simply defining the check for ship function inside the ship methods.js file then i'll export it by typing module dot exports dot check for ship equals check for ship all right so that should be enough to get over the import error so now if i go over to the console and run npm test cool it shows that we're passing our new tests but we know that's a trick because that test doesn't actually contain any expectations yet at least we know our files are connected properly and that we set up our suite and spec correctly those sanity checks are helpful so let's write an expectation for this spec now in the ship test js file for this spec to be meaningful it has to call the function we're testing so it's time to make some guesses about how this function actually works we need to guess what kind of parameters check for ship will accept in order to use it in our spec now we could represent our coordinates in lots of ways a string of two numbers an array an object and so on so i like the idea of arrays since they come with lots of useful methods built in that we may want to use again i'm just making educated guesses right now when i start writing the game the implementation details might challenge our starting assumptions so it might be easier to come back and adapt our tests than it would be to make our functions match our expectations so the unit test we're writing just gives me a starting point that makes sense to me inside the spec i'll write our expectation to call check for ship with a player and an arbitrary location where none of that player ships are located so let's say nine nine and since there are no ships there check for ship should return false so let's add to be false okay so now if we run mpm test in the console our test shows that our spec isn't quite ready we're trying to pass check for ship arguments that we haven't defined yet the hardest part about bdd is deciding how the function we haven't written yet might actually work we have to make an example of the kind of argument our function might expect so that we can use it in our spec correctly this is a lot like how we made up an imaginary array of people objects to test our gather names of function back in stage 1. the difference is that we don't have the real function in hand yet and we're not adding that cruft to our real code files these guesswork function calls and data structures are just going to live here in our test spec isolated from all of our production code the important thing is to focus on the general behavior of the function we're testing and not get bogged down in the implementation details yet for now i'll just invent something that will get the test up and running and kind of makes sense and if i need to revise it later then that's okay at this point i'm guessing it makes sense for players to be represented as objects since they keep track of different kinds of information so right above our expectation i'll create a player object each player will have a set of ships so i'll set an array of ships as a property on the object again there are lots of ways to represent sets but i'll start with an array for convenience since this test only needs us to check one chip that's all i'll add we'll store each ship's location as a property so i'll add a locations array to the ship and set it up with just one coordinate let's say zero zero at this point we're assuming a lot about the structure of players ships and how we keep track of their location it's a lot of guesswork up front but even without writing tests programmers still do a lot of guesswork as they program it's rare that you know exactly how every part of a program works when you first sit down at your computer so it might feel a little strange to write all of these tests before you start writing your program just keep in mind that most people also feel awkward when they first start using outlines for their essays and blog posts if you feel overwhelmed by this setup just remember that you can always work in a tighter feedback loop for example you could have started this setup work by only writing an object named player to make that reference error go away in our test output then you would start with another small step and another until you had something that looked similar to this okay so now i have a player and it has one ship with one location and if i check for a ship at nine nine check for ship would return false because the ship that player has is located at zero zero they don't have any ships yet located at nine nine running the test in the console gives us a new assertion error it says expected undefined to be false great we get undefined because our real check for ship function doesn't return anything yet so it's time to start writing our function so back inside ship methods dot js the check for ship function will accept the parameters player and coordinates first i'll declare two variables inside the function ship present and ship now i know i'm going to have some kind of conditional block because i only want to return false if the player has no ships at the given coordinates i know i'll need to loop through all the ships in the ships array and check each of their coordinates against the one we're giving the function so i'll just put this condition inside a loop over all the player ships next i'll save the current ship within the loop to make things easier to read finally i'll filter the current ship's location array down by comparing each value to the given coordinates so again this will filter the current ship's location array for matches against the given coordinate both the x and y numbers should match so this is going to return an array containing only the coordinates that are a match and if nothing is a match the array will be empty so it will be a coordinate only if there was a match the first member of an empty array is undefined which will fail conditional and that's perfect it means we only need to save the first element of the filtered array and use that as our conditional check so if the array is empty that means there weren't any matches to our given coordinates and no ship is present right below we'll check whether a ship is present at a given coordinate by saying if no ship is present return false all right so now i'll go over to the console and run npm test to see if it works great we get all green checks for tests our check for ship function is looking good but we've only proven that it will report a miss it tells us when no ship is located at our given coordinates but does it also correctly tell us when a ship is located at our given coordinates let's write a new spec that would prove it okay so back inside shiptest.js i'll just copy my current spec for no ship present and paste a new spec right below it then i'll adjust the description and expectation so this description will say should correctly report a ship located at the given coordinates this spec tests whether the function correctly finds a ship that is located at the given location so we should change the argument we passed check for ship to match the player's one ship location so i'll change this to zero zero then i'll adjust the expected result to be true so now when i run npm test in the console the test report assertion error expected undefined to be true well currently the only time our function returns something is when no ship is found so maybe we should add a matching conditional to return true when something is found so in our check for ship function inside ship methods dot js we'll say if no ship is present return false then we'll say else return true all right so let's run the test again and cool now it works but so far we've only tested check for ship using a single ship with a single location so we should expand our test to be sure it still works when a player has more than one ship and when a ship is located at more than one coordinate so again back inside shiptest.js i'll copy the spec at the very top and paste a new one at the bottom then revise my description and expectations so i'll change this description to say should handle ships located at more than one coordinate this spec will test the same behaviors we've already tested but with a ship having multiple locations so inside locations i'll add a new coordinate of 0 1. then i'll create new expectations for this location and adjust the expected result to be true so i'll simply copy this expectation and paste the new one right above it and this will say expect check for ship player at 0 1 to be true now you normally don't want your unit test to have more than one expectation because it might be unclear to someone reading it what the test is supposed to prove now in this case since we have individual unit tests for positive and negative checks i'm comfortable using both expectations in this unit test for brevity in fact i'll also add an expectation for the zero zero location just to make sure we can find both and i'll set the expected result to be true all right so let's save our file and run npm test in the console and great it seems good so far so it looks like check for ships doesn't break when we give it a bigger ship so let's try more than one ship so i'll copy the bottom spec inside ship test and paste a new spec right below it and i'll change this specs description to should handle checking multiple ships then i'll change the expectations but first i'll just add one ship to the ships array with new coordinates so i'll copy the first location and paste the new one right below that and change the coordinates to 1 0 and 1 1. then right above the last expectation i'll add two new expectations for this new location so i'll copy one of these and paste it below and for this i'll say expect check for ship player at 1 0 to be true then we'll add another one that says expect check for ship player at 1 1 to be true all right so let's save this go over to the console and run npm tests so now our tests finally catch a bug it looks like multiple ships don't work correctly so the test passes the first expectations which look at the first ship and the array but the expectations aimed at the second ship failed it says assertion error expected false to be true well we must have made a mistake with our loop logic why else would everything work for the first ship that first loop and then break during the second so let's take another look at our check for ship function and sure enough i buried one of the return statements too deep the loop will currently check for no matches after every ship so that means that if the first ship isn't a match for the given location then the function returns false right away before getting to loop again to the next ship so it looks like we need to reverse this logic a bit we want the function to return false for no matches only after it has checked every single ship so that means it should return false only after the loop has finished but it should return true immediately when it finds a match so we can just switch things around to capture this idea better so we'll say if ship present return true then i'll delete this else condition then right before the closing bracket we'll say return false all right so let's save this and go over to the console and run npm test and great it looks like the test all passed now and to be double sure we can expand the test suite a bit as a proof of concept so let's add one more ship to the ships array with new coordinates so i'll copy one of the locations and paste the new one at the bottom then i'll change the coordinates to 2 0 to 1 and i'll add two new coordinates so this one will be 2 2 and the next will be 2 3. and then i'll go ahead and add a new expectation that says expect check for ship player at 2 3 to be true so now if i go over to the console and run mpm test it works great that was a lot of work so pat yourself on the back for doing a great job in the next video we'll keep adding specs to this test suite and expand our game of battleship we're starting to get the hang of mocha and bdd and we're using it to write more interesting functions so let's expand our test suite and see some other things chai and mocha can do now that we can check for a ship we also need to register damage on a ship currently check for ship only reports true or false depending where ships are located but we need to actually keep track of the damage each ship has sustained too so let's start a new test suite to describe the damaged ship function we introduce a test suite in moca using describe so right below the check for ship suite we'll add describe then we'll pass two arguments the string damage ship and a function then we'll import the damaged ship function from the shipmethods.js file at the top of our suite so what do we definitely know about the way damaged ship should work well it should probably take a ship and a given coordinate of the ship as arguments and we need to know which ship to damage and which spot to register damage on so that sounds like a good expectation of damaged ship so we'll create a test spec that says it should register damage on a given ship at a given location so now that we're dealing with a given ship this function doesn't need any information about the player object so i'll set up the simplest ship i need to conduct the test i'll add one ship one location then i'll add a damage array to keep track of the damages taken so now i'm going to write the minimum function i need to get the test to run correctly so damage ship is different than check for ship in that it doesn't return anything so we can't just plug it into our expect method instead we have to test the side effects of damaged ship after we call it so inside our spec if we call damage ship then pass ship and the coordinate 0 0 what should we expect to be different well we should expect the ship's damage array to be different it should not be empty remember earlier when we wrote our title case function i mentioned that chai has a lot of assertion methods for making our tests easy to write so let's go to the documentation and see what's available for us to use i've linked to the bdd api docs in the teacher's notes of this video so looking through the available methods the very first one i see is not and the docs even give me an example so here it says expect foo to not equal bar well that seems useful i've already described my expectation to myself so the damage array should not be empty so i'll just search the page for empty and see what i get great it looks like chai has a built-in method that checks for empty objects arrays or strings cool so this will make it really easy to write our expectation without worrying about how the real code will work so back inside our spec right after damaged ship i'll add expect ship dot damage to not be empty all right so now if we save our file and run npm test in the console we get a type error it says damage ship is not a function well of course because the damage ship function is missing so let's create it so back inside shipmethods.js i'll define the damage ship function and the function will accept the parameters ship and coordinates and we'll need to export the function at the bottom of our file by typing module dot exports that damage ship equals damage ship remember without these module dot export statements the test files won't have access to the functions we wrote and they'll fail when we run the tests all right so now let's go over to the console and run npm test so now the test reports a new error it says assertion error expected brackets not to be empty so inside the damaged ship function we'll say ship dot damage dot push then we'll pass coordinates so let's go to the console and run the test again and great now the test passes proving that damaged ship can change the given ship's damage array so now we should add an expectation that proves we actually added the right thing to the array so let's see if chad can help us out with this too so once again i'll search the docs now i'm not exactly sure what to call the method i'm looking for here but i do know that i want to check whether an array has specific members unfortunately i can't just expect the first member to equal 0 0 because two unique arrays are never truly equal in javascript even when they store the same values every array is a distinct object but javascript uses strict identity for equals so two arrays could only be equal if they were really the very same object not just two objects with all the same property values we call arrays that look the same deeply equal because their deep interval values are equal even though the arrays themselves are different objects so i'll try searching the page for array and see what turns up all right so the first thing i see is a method called include which looks like it checks for an array for a given value this is perfect all right so back inside shiptest.js i'll add a new expectation to my spec by simply copying this one and pasting a new one below and this one will say expect ship dot damage to include zero zero so now when i go over to the console and run the test again i see that the test doesn't pass it says expected zero zero to include zero zero so i can see that the ships dot damage array includes the array i want but chai doesn't seem to believe it it must be that include doesn't really solve our deep equality problem from before so let's check the docs again now the problem seems to be deep equality so i'll try searching the page for deep and see what comes up great so this method here looks good because it allows us to make deep equality comparisons which is exactly what we need so now i can write an expectation about the first member of the damage array by changing this expectation here at the bottom to expect ship dot damage zero to deep equal zero zero so i'll say two dot deep dot equal so basically just check that the first element of the damage array looks like this all right so now if we save this file and run mpm test in the console great it looks like everything is passing now we could complicate the damage ship function by first making sure that the ship hasn't already taken damage at the given location by making sure the location is valid and so on but this will do for now now we need a method that players can call during the game in order to fire on their opponent it should use check for ship to confirm the attacking player's guess and it should use damage ship to register damage on their opponent try and write a test suite for this fire function using some of the assertion methods you learned like equal not dot equal deep.equal includes and so on you might even check the documentation to find something more useful for your test spec idea the link to that documentation is in the teacher's notes of this video and if you're feeling confident you can even try implementing the function based on your test suite we've already been using bdd this whole time i think you can do it and when you're done i'll show you my approach in the next video the fire function is one of the last things the game engine actually needs isn't it amazing how fast we can build stuff that sounded hard before we started so here's how i wrote my test suite for the fire function all right so first i'll set up my test suite at the bottom of the ship test.js file the test suite is named fire so i'll pass fire as the string followed by a function then i'll import the fire function at the top so it's available to all my specs i'll skip running the initial test in this case because i know it's just going to break at first but it's a good habit for you to keep all right so now i need a spec to test fire's behavior the first thing that comes to mind for this function is that it should record damage on the correct ship if there's a hit so i'll create a new spec that says it should record damage on the given player ship at a given coordinate so now i need to tell fire which player i'm targeting and which location i'm guessing so it should accept a player parameter and a coordinate parameter that means i need a player object for this test so i'll say var player and the player will need at least one ship at one location so i'll say locations zero zero and after the location i'll add a damage array so if i set up a simple player object and call fire at a location i know is occupied so let's say fire player at zero zero the players only ship should have a matching coordinate and its damage record basically the ship should take damage at the given location so let's write an expectation for that so right below we'll say expect player dot ships zero dot damage zero to deep equal and then we'll add 0 0. so if we run the test now in the console we can see that the test breaks at first so now let's go ahead and set up the fire function in shipmethods.js so right below the damage ship function i'll create a new function called fire and we'll pass the fire function two parameters player and coordinates then i'll go ahead and export the fire function at the bottom of our file by saying module exports dot fire equals fire so now if i go over to the console and run mpm test we can see that the test gives me an assertion error it says expected undefined to deeply equal 0 0. now the fire function doesn't yet do anything to the target ship but i know that in the end if a ship exists on my opponent's board at the location i guessed i want to run damaged ship on that particular ship using the location i guessed when calling fire so i'll start with check for ship using my guess and the given player since this returns a true or false value so inside my function i'll save it to a variable named ship to use it internally then i'll pass player and coordinates as the arguments so i don't need to worry at all whether check for ship will work here because my test suite has already proven that it does exactly what i expect and that's super awesome it takes a huge load off my brain it's kind of like knowing that a jquery function will just work and you don't need to second guess it so here i'm calling the variable ship because that's what it represents okay so if my opponent has a ship where i guessed then i want it to take damage so i know which coordinates to pass in but how do i know which ship to pass now i could run through all my opponents ships and look for the one i want but that's already what check for ship does and i don't want to repeat all that logic here so instead let's just go refactor the check for ship method remember when i warned you that we might have to adjust our functions as we go so it's okay to throw away code that was working even if we spend time writing tests for it already in fact this is one of the biggest benefits of having unit tests red green refactor even though our tests for check for ship are passing it turns out we need check for ship to work slightly different now so our unit test will tell us exactly what breaks and how as we rebuild things so this is an easy fix i'll just have the check for ship function return the actual ship instead of simply returning true when finding a ship so i'll replace true with ship and if check for ship finds something then i want to call damage ship on that ship at my guest location so in my fire function i'll say if ship then damage ship and pass the ship and the coordinates all right so let's see what the test reports in my console i'll run npm test well this is interesting it looks like all my check for ship tests are now failing because i've dramatically changed the way check for ship works but it looks like my fire test passes so that's good so back inside shiptest.js scrolling all the way up to my check for ship suite i can quickly fix my test up now my first test is fine because it still returns false when it's finding nothing but i'll change my next specs expectation to deep dot equal then i'll pass player dot ships zero all right let's go over to the console and run the test again and i see that my test is green now for that particular spec so great now i know the strategy works now moving forward for all tests to pass we'll need to make similar deep equality comparisons in the check for ship suite so we'll edit some of our other expectations to expect an array instead of true now we can simply copy and paste this deep dot equal expectation across the rest of the check for ship suite change some of the values to match the first member of an array and everything should work great all right so i'll copy this deep.equal method from this expectation then scroll down to the next spec then replace the first and second expectation we'll leave the values at zero then scroll down to the next spec and replace all of these as well so we'll change the first one from to be true to deep dot equal player ships 0 we'll do the same for the next one and this one will change the value to 1 and we'll do the same for the next one change that to 1 and finally we'll replace the fifth one and change this value to two finally i'll scroll down to my fire suite then i'll create one more spec here inside the fire suite by copying the first spec and pasting a new spec right below i'll change this new specs description to should not record damage if there is no ship at my coordinates so when i change the target here to 9 9 i would expect the damage array to be empty so let's change the expectation to say to be empty then remove the second zero value here after damage so let's see does fire now correctly leave the ship on damage if we guess wrong well let's see i'll go over to the console and run npm test and yep the tests already pass now i might also want to add some reporting to the fire function so that players know whether they hit or not since this is just a game engine we'd probably have a separate reporting function that did fancy stuff with a dom or printed a useful message in the console for now this looks really good we have almost all the major logic for the game in place did you use other assertions in your test did you build your fire function differently share your solutions in the community and compare them to what you see here in this stage we've learned how to set up our files for mocha the main file structure of a test suite in mocha and a lot of useful chai assertions to make writing tests easy we also did a lot of bdd to outline the basic logic of a battleship game engine it can feel strange but with practice you'll find that it really is super helpful to outline your ideas first we were able to refactor things easily and know exactly what changed in our code base manual testing can never give us that level of confidence in the next stage we'll learn some great features of mocha for reducing the amount of test good we write while also improving the quality of our test output writing unit tests might seem like magic sometimes but in the end we are still just writing javascript you'll want to follow the same best practices you use when writing any javascript program we should keep our tests dry and tidy in this stage we'll explore some handy ways to avoid repeating ourselves we'll also expand our tests to build our confidence and the code we've written so far so far i've been writing the dependencies for every test within each spec for example in our check for ship test suite i wrote a new player object for every test so the first test has a player and the second test has the same exact player so we need this code to simulate a player object to test the check for ship function in other words the check for ship function doesn't work without a player object so we need to fake or mock a player object in order to test the function so as you can see duplicating this code in our test file goes against the programming best practice of don't repeat yourself or try fortunately mocha like all testing frameworks comes with a few useful functions to help us set up conditions for our tests like creating test objects and simulating the conditions inside our app this is called the setup phase the part of our tests where we set up conditions for testing in fact mocha splits the setup process into two blocks the stuff we set up before the entire series of tests and the stuff we set up before each individual test let me show you an example the check for ship function doesn't alter any other code it doesn't have side effects on our player object or ships it doesn't make changes anywhere it just does some calculations and then gives us a report back it says false if there's no ship at the given coordinates or it tells us which ship it found if there is one so because check for ship won't change a player object in any way it's safe to set up a single player object once with the information we need and then use that for all of the check for ship tests so the first thing i'll do is initialize a player variable at the top of the test suite so right after i import the check for ship function i'll type var player then right below it i'll set up a before block so before just needs one argument a function that runs before any of our test specs to set up any state they need in this case each test spec needs a player object with a ship's array so i'll simply cut the player object out of this first test spec and paste it into my before block so now the first spec will have access to the same player it did initially and before anything else happens in my test suite mocha will make a player object for all of the specs below to use pretty cool so now i can go ahead and delete the player object from my second spec 2 because it will use the same player object the first one used now to repeat this for the other specs we have to make sure we're keeping all the added complexity those tests need in this player object so for instance when i get to the third spec i see that the player object up here in the before block is going to need a bit more complexity for our test suite to work because i see that this ship needs a second location and in fact the next test spec needs a bunch of ships so what we'll do is take the most complex player object in this suite and use it up here in our before block so the last spec in the check for ship suite represents the most complicated object we need for any of our specs so i'll cut the ships array used in this last spec and replace the one up top by pasting it into the before block alright so now all the tests in my check for ship suite will use this player object but we only need to write it in one place and it will work for both my complex test and for all the simpler tests too so now i can go ahead and delete any player object in my check for ship specs so now since all of my test specs have access to the player object and the before block if i run the test in the console great all my tests still pass all right so let's clean up the fire spec now the fire function is different than check for ship fire is not a pure function it has side effects in other parts of our code base in this case fire changes the damage array of the shipped object it's given by calling damage ship so if we ran it over and over we would just keep pushing damage records onto the same ship so that means that our test specs will be altering the application state from one to another which will mess up our expectations and maybe make them fail even when our function works so instead of setting up a ship once in a before block like we did earlier we'll reset the ship for each spec to do this we can use a before each block so first i'll initialize a player variable at the top of the fire suite so right after i import the fire function i'll type var player before each works just like before only it will run the function before each spec instead of just once at the start of the whole suite so right after the player variable i'll set up a before each block if i copy just the player object below into the before each block so i'll go ahead and remove var that means that the player object will be overwritten before each test so no matter what changes happen inside of the first spec it will all be erased and set back up new for the next spec right here that makes our application state predictable between specs even when the function we're testing has side effects a nice bonus of using before and before each blocks for setup is that our suites feel easier to read we can see exactly how the test author thinks this function needs to work it acts like a little bit of documentation for how the function is intended to be used so if i save my file and run the test again in the console nice all my tests still pass so in the next video we'll cover the last phase of a test suite tear down once your test suite runs you won't need the setup variables or objects any longer in fact if you leave those around they might interfere with other tests that run later that's why moca provides a tear down phase to remove unwanted variables in addition if your tests change your development environment like creating a pretend database or start up a local server you can use the teardown block to set your system back to where it started you'll need tear-downs more often when your tests need a pretend database or they interact with a dom somehow for example you might be testing a function that sets up a new remote database creates a local file or starts a server instance when these functions are doing their job they end up polluting your database or host service with a bunch of junk testing tables and files in that case you'll want to take apart any changes your tests make so that the test further on start with a clean slate and your external systems don't get crowded with test results sometimes that really is what you need to do but if you find yourself depending heavily on the tear down phase you should double check that you're testing the right kind of function for example developers often rely on an external library like jquery or react to deal with the dom we shouldn't be writing tests to make sure the add class function works correctly that function has already been thoroughly tested for us we should also definitely try to avoid passing state between our test suites that adds unnecessary complexity and room for error remember our tests should not be clever they don't need to do anything fancier than report our basic expectations our battleship engine doesn't rely on an external database or write files to our machine it only needs a few objects in memory we saw in the last video that javascript objects can just be overwritten in the before and before each blocks so instead of destroying our object i'll show you how to use the after and after each hooks to print a message in our test output mochas after and after each hooks work exactly like before and before each except that they only happen after so in the fire suite right below the before each block i'll add an after block by typing after now after takes one function that will run at the very end of the test suite when every spec has finished inside the function i'll add a console.log that says entire test suite completed so now right below the after block i'll create an after each block by typing after each and just like after after each takes one function that runs at the very end of each test spec so inside this function i'll add a console.log that says one unit test completed all right so i'll save my file and if i run my tests in the console by typing npm test you can see the message one unit test completed after each individual test and then we see entire test suite completed at the very end of the block perfect writing tests first as an outline can help us a lot in the development process sometimes you'll also have to write test for existing code it might have been written by other people or you might have skipped the testing phase during a chunk of your workflow you should write tests retroactively just the way we have been in bdd decide what the function does and focus on that part rather than the implementation details write simpler expectations first and get them to pass before you write more involved ones one difference is that you might get to testing for an edge case faster than you would during bdd an edge case is a radical situation your function might end up in but it isn't how your function would normally work for example an email validator might work when users type properly formatted emails but what if they type in nonsense by accident an x-ray machine should accurately produce the amount of radiation a doctor asks for but what happens if a doctor accidentally asks for a huge amount so far in our test suite we've assumed that our functions will get the right kind of input for example we always call check for ship using a valid player object and an array with two numbers but what if something changes later or someone tries to use our functions in a way we didn't intend to show you what i mean i've added a new function to our engine to follow along using the latest workspace launch the workspace on this page or you can download the files if you're working outside of workspaces so players need a way to put their ships on the board for each ship they should be able to name a starting square and a direction and save the ship's location for the game in the latest files i've added a test file in the test directory named playertest.js this new file contains a test suite for each of these new functions so here we have validate location then validate locations and right below place ship now since you haven't seen these functions yet it's a good idea to run the tests first and see what information they give us about the new functions so over in the console i'll run the new tests in the player test js file only by typing the command mocha test forward slash playerunderscore test.js so immediately we get a lot of information here we have the three related functions validate location validate locations and place ship now validate location sounds like it confirms if a single given coordinate is not occupied yet and is on the board and validate locations sounds like it runs validate location on a list of coordinates and place ship takes a ship and change its starting location it responds with false if the proposed location would run off the board or overlap another ship so without even looking at the code i have a good general sense of what these functions do how they work and how they're related so now let's dig into the test code so this is a new idea this time i wrapped the entire test suite inside a describe block that names the suite that makes it easier to see that these tests are grouped together when i run npm test and all of my test results are printed to the console at once as you saw earlier inside the main describe block i have tests for the validate location and validate locations functions feel free to pause the video and read these carefully but we can skip over them for now instead let's focus on the main function being tested play ship so in this suite i import the place ship method then set up a new player object before each test spec and notice that the player ships have a new property of size and there's one test spec in place here it places the player's first ship at 0 1 and expects the ship to have one location at 0 1. again we haven't even looked at the actual code for this function at all and we already have a good understanding of what it does and how it works so we know that it works when everything is used as expected but what happens if it's used in some way we didn't expect for example the function expects four arguments now it's conceivable that someone might forget to use them all in the future for instance what happens if we don't pass a direction will the function throw an error and crash the game will it quietly add some nonsense like undefined or not a number into our ship's location or will it return a request for a new location excluding an important argument like this is an edge case ideally this would never happen just like ideally people would always enter valid email addresses into forms but as developers we can imagine it happening pretty easily so it might be nice to test against this possibility now we don't necessarily have to build a handler into this function but we should definitely know what to expect when it happens having a test in place will demonstrate to other developers or our future selves what we're thinking about in this case it might also provide useful guidance to them if they do decide to handle this case in the function later it will be like we wrote a bdd outline for them already so inside the place ship test suite i'll create a new spec right below the existing spec i'll name this spec should throw an error if no direction is specified so now let's check the chai docs to see if there are any useful expectations i can use here first i'll do a search for air now the first result shows up under the method not but i see something that looks useful here here it says to not throw air now i don't quite understand how to use the throw method yet so i'll do a search for throw too and cool this tells me everything i need to use the throw method with lots of examples so it looks like the throw method requires me to wrap my function in something that chai can handle internally now if i didn't do this then my test spec would always fail because play ship will throw an error mocha will think that error counts as a test failure even though the air is really what i expect so inside this new spec i'll trap the error in a new function called handler that chi will manage from within and prevent the air from failing the test so we'll save our handler and inside this function we'll call play ship and i'll also go ahead and save the ship and coordinates variables inside the spec that way the function call below will be a little easier to read so now i'll pass only three arguments to play ship with no direction string specified we'll pass player ship and coordinates and now i can expect the handler function to throw an error so right below we'll say expect handler to throw error so now if i save my player test file and go over to the console and run the same test as earlier we can see that the test fails because the place ship function doesn't currently throw any errors so i'll open up the playermethods.js file inside the gamelogic folder and add a line at the top of the place ship function so we'll say if there's no direction specified throw an error it's nice to give errors a useful message so i'll pass one in here we'll say you left out the direction i need that for math so now if anyone ever messes this up in the future they'll get an informative error about the problem and if i go over to the console and run the test now you can see that everything passes perfect so the chai docs for throw showed me that i can also specify the error message i expect so back inside the new spec and playertest.js i'll write an extra expectation that specifies that so we'll type expect handler to throw then i'll write the error message i expect which is you left out the direction i need that for math predicting edge cases can be challenging i could imagine all sorts of ways that our functions so far could fail web development is hard so pretty much anything can go wrong now i don't want to pollute my test suite with tons of edge cases that don't really have to do with the main functionality of my functions so instead you should spend a little time thinking about the edge cases that are more likely to come up think about problems that would be really hard for another developer to notice or that would break your programs really badly if you can run a test for an edge case or two like this that's often enough at first pushing yourself to think about the problems that other users or developers might have when dealing with your code will also make you a better developer and result in better software so it's good for you too one of the best things about bdd is that our code is always testable it has to be because we always write our test first but if you end up with code before tests it can be hard to make a meaningful unit test for existing functions that's because some code is not testable the particular nature of javascript means that we have access to some code and not other to demonstrate this in the latest project files for this challenge i've written a pair functions that handle a computer player to make a computer player we have to give it a programmatic way of placing ships without us knowing and guessing locations during their turn so i've named these functions computer play ship and computer fire i want to write tests for these to make sure they work but the problem is that they produce random results and that's the whole point if the results weren't random then playing against the computer would be predictable and no fun so if i write a new suite for computerfire in playertest.js what would i expect to result from the function if the random location it chooses has a ship then i should expect that ship to be damaged but if the random location does not have a ship then i should expect the ship to not be damaged i can expect that one of those will happen but that's not very meaningful it wouldn't tell me much about what the function is doing to say expect ship.damage to have length of one or expect ship.damage to be empty so the way computer fire is written is not testable the only tests we can write for it are trivial and uninformative but we know a lot about testing now so by looking at the two computer functions i've written maybe you can find a way to refactor them so that they're more testable try to rewrite the set of functions so that we can still generate random functions now you might need to move the code generating random numbers into their own functions in order to isolate the problem and in the next video i'll show you my idea for getting the code to be more testable so the problem with computer fire and computer place ship is that they have random outcomes built in each of these functions really does two different things first they build a random location then they do something with that location like fire a shot or place a ship in programming we call this tight coupling when two distinct functions are bundled up so that they can only really be used together now a hallmark of tightly coupled code is that it makes our job of writing unit tests harder so to refactor this code i think we can actually just abstract those two ideas apart we see that the same code appears in both the computer fire and computer place ship functions the part that generates two numbers in the board range and puts them in an array so we can pull that out into its own function that returns the result let's call the function get random coordinates then i'll cut the variable declarations for x y and coordinates out of the computer fire function and paste them inside getrandom coordinates so now to put the x and y numbers in an array instead of var coordinates equals xy we'll say return xy so now i can delete these from computer place ship as well so computer play ship also randomly chooses horizontal or vertical so we can use the same strategy by pulling out that logic into a separate function let's call this function get random direction so now i'll go ahead and cut the direction declaration out of computer play ship and paste it inside get random direction and in this function instead of var direction equals math.random we'll say return math.random so at this point there's actually nothing left of our original functions besides a call to fire and place ship so at the bottom of our file we can remove the module.exports for computer play ship and computer fire but now we can also use the randomizers with the existing fire and place ship functions in place of the original computer functions so for example we can set up the game to call fire and play ship with some attempted random coordinates in between human player turns like this we also get to use these randomizer functions in other parts of our game engine if we need to that's a big plus more reusable code will save us time later and this way we don't have to add any tests at all in fact this is another thing that writing test helps us with when it's hard to write tests for something you might wonder whether you really need that function in the first place and this way writing tests can guide us towards simpler code we still can't do a super meaningful test for the randomizer functions because of their random nature but we've pulled the random pieces apart from our other application code so that our other parts remain testable thinking about good unit test has improved our code yet again making it more reusable and modular we've learned a lot in this stage set up and tear down in our test suites testing for edge cases and keeping our code decoupled and testable so you're ready to use mocha and chai to keep doing bdd in your applications in the last stage i'll show you some other convenient ways to display your test results and introduce some more advanced features of mocha we've covered a lot so far using bdd to lay the groundwork of our battleship game using mocha and chai to write unit tests for code that already exist setting up test suites and test specs with mochas describe and it blocks and writing test expectations with chai's expect method but there's a lot more that mocha can do in this stage we'll wrap up by looking at some of mocha's extra tools let's get started so far we've been looking at mocha's default reports in our console they're much easier to read than standard javascript console.log statements but there are other options too for instance you can change the reporter style using the reporter flag when running mocha a good use of the reporter flag is when you only want to see test failures when you have lots of tests running all the time you might not care to see all the specific passing tests in your console so to show only the failing test errors you can use mocha dash dash reporter min so in our console now mocha only prints 15 passing but if i change one of our expectations to fail a unit test then mocha will print the same error report we're used to seeing and hides the passing tests so for example in main test.js i'll change expect true to be okay to expect true to be false so that it fails so when i save the file and run the same test again mocha now prints 14 passing and one failing followed by the usual air report so another cool reporter is the markdown reporter moca reporter markdown will print the same long test report we're used to but using markdown format that means you can just copy and paste it into the readme file for your github repo and use the passing tests as pre-written documentation this may help other developers understand how your code works without them having to download and run the tests go ahead and check the teachers notes to learn more about the markdown format if you'd like to learn more you can browse all the possible reporting mechanisms in the mocha documentation now some of them are just for fun and others like markdown and min are useful in different situations so try some out and share your favorites in the community i personally like the nine reporter i think it's pretty awesome and if you find one you like better than the defaults you can add a reporter flag to your package.json file and your test command like this way npm test will always use that reporter in some ways being a programmer is more like being a writer than being an engineer it requires a lot of creativity and you can get stuck a lot waiting for inspiration to strike a nice feature of mocha is that we can outline tests that need to be written still before we're ready to write them so when you have an idea for a test but you don't have time to figure it out or you aren't yet sure what it will look like you can still communicate your idea to other people and get started let me show you an example in order to play a game it would be nice to have a function that checks whether the game is over after each turn that way every time i make a guess the game engine will check and see whether i've struck the last ship and won or whether the next player should start their turn i know i need that but i'm not ready to think about how it works yet not even to write the tests so i can write a pending test spec to keep track of those tests i have to write later inside the test folder i'll create a new test suite file called gametest.js since this suite will focus on functions pertaining to the flow of the game so first i'll import chai at the top of the new file so that we can use it here as an expectation and now i'll set up the fundamental describe block for this suite and as the first argument i'll write game instance functions in all caps this is only here to make the output nicer for me i'll call the function i'm testing here check game status so i'll create a new test suite for it now a test spec that i know i should write for this function confirms that it should tell me when the game is over so i'll create a new spec that says it should tell me when the game is over this is a pending test so in order to mark this test as pending i do not add a function as the second argument the it block is only written with the first argument describing the test and it doesn't contain any expectations yet so now if i go over to the console and run npm test it shows me that i now have 15 tests passing and one pending notice the pending tests are in a different color so they're easier to see in my console the test is light blue this is really useful as a reminder to myself and others that some tests need to be written still it's like having an outline for my outline it communicates some information for getting started i can also mark tests as pending by typing an x in front of the pending block like x describe or x it for example if i add a regular test spec to my current suite even though i've added a function as a second argument i can add x in front of the describe block and all of the specs inside will be marked as pending i prefer leaving out a callback to using x describe because i don't have to delete anything later i just have to continue writing the test spec as i normally would you might see this done both ways so feel free to use whichever method you like better if you're like me you might be annoyed that you have to type npm test every time you want to check whether your changes pass or fail the tests so mocha comes with a special watch flag that will automatically run any specified tests when it sees changes in a certain file or directory for example when you launch the latest workspace or open the project files for this video you'll see that i filled in a spec we started writing in the previous video for the check game status function but i still haven't finished the function itself so when i run check game status on a player with one sunken ship then the game should be over and check game status should report false so while i'm building this function i want mocha to automatically run my tests every time i make a change to check game status so that i'll know right away when all my tests are passing we can do just that with the watch flag i've set up the initial function in this new gameinstance.js file located inside the gamelogic folder and running npm test in the console shows that the test fails so it really breaks up our workflow when we have to write code switch to the console run npm test see the output and switch back we might make a lot of changes to our function and it would be nice to know right away when something works or something breaks so to watch my gameinstance.js file i run mocha with the watch flag i need to tell mocha which test to run and which file to watch so in this example if i run mocha dash dash watch followed by the path to the game test file then the path to the function file mocha will run the suite found in gametest.js every time i save a change to the file it runs right away and i see the original failure so inside gameinstance.js if i set the check game status function to return false and save the file we see the console automatically runs the test again and now they're passing pretty cool so you can write custom mocha watch commands like this whenever you plan to be working on one particular file a lot but you can also save a general watch command in your package.json file to run your tests all the time while you work that might be helpful for you to check whenever you're unsure what's working and what isn't working in the big picture while you build your application so to set that up let's go ahead and open up the package.json file so in your script section after test let's add a new command so first we'll type test colon watch the command we want here should run all of our unit tests whenever we make changes anywhere so let's try mocha dash dash watch followed by space then period for slash test followed by space and period forward slash remember the first argument describes the test we want to run all the tests in the test directory and the second argument describes the files we want to watch for changes so all the files in the current directory and don't forget to add a period at the start of your file path or mocha will get confused on which files you're looking for alright so go ahead and save my package.json file then bring up the console and now when i run npm run test colon watch mocha starts running all my tests and watches for any file changes in my project so great i see all my tests passed at first so now if for example i open up the main test.js file and change an expectation to fail so let's say expect true to be false when i save the file we immediately see the failing test results in the console and if i change it back to ok save the file i see the passing test results again and if i open a game logic file like playermethods.js and comment out some important code once i save the file i see the tests are failing again so if i uncomment the code and save all my tests pass again cool so this is really useful and now to stop running watch you can go over to the console and type control c so if you have a lot of tests you might want to adapt this command so that it doesn't run them all every time you make any change you can change a command to be more specific to the part of the code you're currently working with to save time and improve performance and in your package.json file you can even set several different commands like test watch player methods and set it to watch only playertest.js and playermethods.js that way it's easy to watch different things at different times you've seen a lot of testing material let's wrap up with some talk about advanced testing features you might use in the future a big part of writing effective tests is making sure that your tests focus on one clear behavior if your tests have a lot of external dependencies then it's hard to know whether they're failing because of the function you're meaning to test or if actually one of the dependencies is failing that makes the test less powerful in debugging because you might have to check functions other than the ones named in your test spec you may not be sure where to start for our outlining we've also seen that it's sometimes helpful to pretend that we know how certain functions work in order to get started we can take this idea even further with mocks and stubs mocks and stubs are two special kind of fake helpers for our test suites we've been using fake helpers all along with the fake objects we set up before each test mocks and stubs are fake functions that fill in the gaps for our test unit's dependencies now developers don't always agree about the difference between mocks and stubs but i've posted some links in the teacher's notes that you can read to get a sense of the distinction in this video i'll show you a stubbed function to get you started thinking about more complex testing we still need a function that will let players take their turn that function should ask the current player to guess a location fire on that location and then check whether the game is over afterwards if it is then it should return something to stop the game because the game is over and if it isn't then it should return something that lets the next player take their turn notice again how i don't really know yet all the details of this function i'm just making educated guesses about it to describe its general behavior so in gametest.js inside the game instance function suite i'll create a new test suite for that function i'll call it take turn so here at the top of the suite let's import the take turn function that will eventually be written in the game instance file for my first spec i'll test that take turn returns false when the game ends so we'll say it should return false if the game ends this function combines several behaviors guessing firing and checking the game's status we've already tested fire but we haven't yet implemented any functions that take input from the player that's because we don't know yet about how it will be played will it be in a browser will it require manually typed commands will it use a game controller so we've kept our game engine in the dark about these questions so that we can use it in a variety of environments so using stubs will let us test logic that depends on these decisions without having to actually make them yet for example the take turn function will probably call a guess function to decide what happens next since i haven't written these yet i can introduce a variable called guess at the top of my function just like i've been doing for player and ship objects so next i'll create a before each block right above the spec here in this block i can set guess to be a pretend function that doesn't do real game logic now i call these pretend functions because they just return the values i would expect the real working functions to produce guess will gather a location from the current player that they want to fire a shot on this could be from a click in the browser or typed in the command line or some other method maybe it will even be spoken into a microphone who knows so no matter how players interact with guess it will somehow need to return a valid coordinate so that means i need to set guess to be a function that returns a valid coordinate so inside the before each block we'll say guess equals function and inside the function we'll return the coordinate 0 0. so now no matter how i write guess in the future this test ensures that take turn does what i expect with the return value finally take turn needs to know which player is taking their turn so i'll initialize a player variable at the top then pass in a player object in my before each block i'll set a ships array as a property on the object then i'll add a locations and an empty damage array to ships so now inside the spec if i call take turn with a player and the player guesses the last ship location of their opponent the game is over so i'll call take turn and pass player and guess as arguments and save it to a variable named actual and again if the player guesses the last ship location of their opponent the game is over so in this case take turn should return false so we'll say expect actual to be false so now even though i have no working guest function i've set up the spec to pretend that those parts of my code already work that's the only important information this test needs if i bring up my console and run my tests my test fails it says take turn is not a function so now i'll jump over to gameinstance.js and write the take turn function here so right below check game status we'll say function take turn the take turn function will take a guess function callback and it will use this to save a guess so we'll save guest function in a variable named coordinates the function will also take an opposing player callback to fire on the opposing player at those coordinates so we'll also pass opposing player then in the function we'll call fire and we'll pass opposing player and coordinates as arguments so now up top we'll need to import the fire function from shipmethods.js so next to check whether the game is over we'll declare a game over variable which calls check game status and in the end the function returns the resulting status of game over so that we know whether to let the opposing player take their turn so we'll say return game over and finally we'll export the take turn function here at the bottom of the file by typing module dot export dot take turn equals take turn all right so let's save our file bring up the console and run our tests so great even though guess has not been written my test passes the test spec used my stub version of the guest function instead so it got all the values it needed just for testing my logic so now i know that the logical structure of take turn is correct so i can move on to writing something else another benefit of this stubbing is that even while i'm working on the check game status and guest functions tinkering with them to do this or that my take turn test will keep passing take turn gets the value i ultimately expect those functions to return no matter how those functions actually work there are whole libraries dedicated to making stubs easy to write like sign in dot js sign in lets you set up pretend servers http requests databases and more so if you want to learn more about mocks and stubs you can check out simon's homepage to see many more examples testing asynchronous functions can be a unique challenge our battleship game doesn't have any asynchronous elements but we might want it to for example if it were played across an internet connection on two computers one player would have to wait for the other's turn if we wanted to save our game somewhere and start again later we'd need to connect to a database that stored our game state those processes would probably be synchronous for efficiency moca allows us to say that a test pack or test suite is asynchronous so passing an argument to the internal function of a describe or it block will tell mocha to wait on running our expectations until we specifically say so let me show you how it works i'll create the function inside the main game instance function suite right below the take turn suite now in this function we'd normally have to write some code to communicate with a remote database but instead i'll simulate that idea with a set timeout function so set timeout will perform whatever function we describe after a specified number of milliseconds if our game really communicated with a database the set timeout function would be replaced with something that sent a request to our database so i'll set the function to run after one second has passed or 1000 milliseconds so after one second the database function would finish and perform some callback like telling the player that the game was saved successfully so let's pass callback as a parameter of save game then we'll call callback inside settimeout alright so now we can create the test suite for save game so right below the save game function as always we'll create a new test suite by typing describe and we're going to name this one save game and then i'll open my first spec for this function inside the suite so we'll say it should update save status and now i have a space to write some expectations about how save games should work so to test synchronous functions like the ones we've written before now in the course i would run the function inside of my spec and then write an expectation about something that changed as a result so to use that strategy in this case i might try running save game with a callback and writing an expectation at the end of the callback to capture the new results so let's do that at the top of the spec i'll save the string game not saved in a new variable named status then i want save game to change the status to game saved when it finishes again in reality that would mean the database responded successfully but in this example it's just going to wait one second and then use my callback function to update the status variable so inside save game we'll say status equals game saved finally i'll try expecting the status to equal game saved after i've called save game so right below save game we'll say expect status to equal game saved so now if i bring up the console and run my tests we can see that the test reveals a failure the expectation runs before the save game function is called because save game is waiting one thousand milliseconds to fire the callback that updates status so that means status will still equal game not saved until an entire second has passed which we can see it's much longer than our test suite takes to reach the expect status line so to fix this problem i need to move my expectation inside the save game callback so that it only happens after save game has finished updating status so now if i go back to the console and run my test at this point it looks like the tests pass but that's kind of tricky mocha is just running through the test function before save game finishes and then it reports that the test spec passes because it didn't see any expectations written so for example even if i write a definitely failing expectation into the save game callback like expect true to be false moga still passes our tests so we need to wait to tell mocha to wait until save game has finished running in order to check our expectations to do that i can pass a special argument to our test specs callback function by convention we call the argument done so that we know the argument is signaling mocha that our test code is ready to be run after some asynchronous operation we normally just use the test spec callback to contain our test code and expectations but by passing it an argument we signal tomoka that we want this test to run asynchronously so now i'll delete this example expectation then run npm tests and now we see that the test gives me a new error it says save game should update save status then the error says timeout of two thousand milliseconds exceeded ensure the done callback is being called in this test so it looks like the air is asking for me to call the done callback passing the done argument to our test spec tells mocha that it's supposed to wait for our instructions before checking our expectations so we passed the done argument but we haven't used it to tell mocha when our code is ready to be tested so in the save game callback i'll add done after the changes i expect to occur and now mocha will wait for done to fire before checking the expectations so when we run the tests now great everything works and notice that there's a short pause while the tests are running that's mocha waiting one second for the set timeout function to run before checking the expectations writing asynchronous code and testing it correctly is a big topic usually you'll write mocks and stubs for your database or ajax calls because those are big external systems that you don't want to mix into your test unit just know that moca gives you a way to deal with these problems so you might need to research the best way to capture the test results you want when you encounter them so congratulations getting started with javascript unit testing good unit testing takes practice there's a lot more you can learn and do to improve the quality of your test and also to make writing tests easier if you've never written any tests this testing first approach is different from what you're used to and at first it can feel like a lot more work as you get better writing unit tests really will improve your code it will also save you lots of time fixing bugs and explaining things to other developers so dig into mocha and chai and let us know in the community when you find useful tips and tricks good luck and happy testing
Info
Channel: Traversy Media
Views: 84,933
Rating: undefined out of 5
Keywords: unit testing, javascript testing, team treehouse, javascript unit testing, js unit testing, unit test, tdd, bdd, mocha testing, mocha js, Behavior Driven Development, Test Driven Development
Id: u5cLK1UrFyQ
Channel Id: undefined
Length: 151min 56sec (9116 seconds)
Published: Tue Oct 27 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.