 This is a workshop and the workshop is dead simple testing with Mocha. My name is Chris Hiller and here we are at OpenJS World 2020. Let me tell you a little bit about myself. I'm a developer advocate at IBM, a NodeJS core collaborator, a Mocha maintainer, so maybe a subject matter expert and an OpenJS Foundation Cross-Project Council voting member on GitHub. I am BoneSkull and on Twitter I'm BoneSkull with a zero instead of an O and to the right is my avatar, which is like a smirking orange skull in a black circle. And so before I talk about this workshop, I need you all to install some stuff. You probably have most of this installed, if not all of it. But to do this workshop, you're going to need Node version 12 or newer. Get version 2.0 something. I don't think it matters too much. You're going to want actually, you aren't going to need a web browser, but you will need a command line terminal app and you're going to need a text editor or an IDE of your choice. So while I'm you know talking here, please download those things, get them installed. So all right. This is the outline of this workshop. I'm going to start by talking about some of the conventions that we're going to use here and how this is going to go. Next I'll get you set up with the materials. Then I'm going to talk about testing, kind of just fundamentals of testing. I'll walk you through writing some tests and then finally we'll take those tests and use them with Mocha. So things about this workshop. So this is how it's going to go and what you can expect who this workshop is for. So you may be wondering if you should be here. So yeah, I would say probably, but I need to make some assumptions about where people are at and those assumptions are here. So you will need some JavaScript fundamentals. I'm not going to teach JavaScript. You should be able to do some basic stuff on the command line. You should be able to navigate directories, move around through the file system. You should be able to install packages through NPM. You should be able to run Node on the command line. You need some way of cloning a Git repository. You need to know how to clone a Git repository. But the one thing that you don't need to know is how to write a test or how to use Mocha. And so there's much formatting and stuff in these slides. And I will post the slides after this workshop here. Unfortunately, it looks like you may need to type some things. So these are the conventions. So a keyword, it will be displayed in bold. And that is just some terminology that will do my best to define. Inline code will be in a mono space font. Emphasis just means emphasis. It doesn't really mean too much other than what it means. File name is displayed in a mono space. But in black, the name of a module, so a module on NPM, a built-in module is going to be in italics. A command that you are expected to run in a terminal or your shell is in green in a mono space font. Link is kind of light blue and underlined. And importantly, so this one in red, it would be in red bold text. These are instructions for you. And so these instructions will say, okay, this is what you need to do to move forward in the workshop. So there are going to be little exercises. And everything that you need to do is in red. Source code will be on the screen. And it will be in this blue box. And it will be syntax highlighted. Terminal output will be in this retro green with a black background. It's just for showing you expected output. You're not expected to copy it or anything. All right. So those are the conventions. So I wanted to talk about what testing is. So testing, software testing is a very broad field. There are many, many, many ways to test software. There are many reasons to do one over the other of types of testing. And so what does it do? So software testing tells us about the quality of software for some definition of quality. And so that's kind of up to you and your team and in your project. A test written by, a test generally written by humans, but a test executed by a human is a manual test. So maybe that's sending things over to the QA team and they poke around your website and they try to break stuff. That's a manual test. Test executed by a computer. These are automated tests. And so automated tests, they make assertions about software. And assertion is, I suppose, the dictionary definition is you are checking something and you will say yes or no if it passes that assertion. And the way this is done is writing more software. So you have your software and the automated tests are more software to test that software. So this workshop is about automated tests. Not manual tests. So why would you even want to do this? So tests avoid bugs. We want to test our code to assert it does what we think it should do. We want to validate our assumptions. We want to, if you know your intent, you want to make sure that the code reflects your intent. When software is well tested at the right level of abstraction, it helps you refactor your software because you'll know if you did something wrong and you'll know if you've broken an interface or a contract. When software is well tested, you can be confident about making changes and releases and pushing that code live. When you have a green build, you feel better about it. When you are writing well-tested software, you're going to have a better velocity in the long term as it will reduce the time that you and your team spend fixing bugs. And you will have time to do other stuff like make more cool things instead of fix the broken things. But it's not, you know, all, you know, it's not all roses, right? So there are reasons why you wouldn't want to write tests. So one thing can be, and if you've ever been in this position, you know, so a legacy piece of software, an older piece of software that maybe doesn't have tests, maybe exceedingly difficult and time-consuming to test and to write tests. Manual tests are probably going to be much easier for that and it may be very difficult to get to the right level of abstraction with testing a legacy code base because there's such a thing as testable code and the legacy code base may not be testable. Adding more tests does not mean that you are adding more value. It's not like a linear, they're diminishing returns. So at some point, you're going to get to a point where adding more tests doesn't really help and instead it just introduces the third thing in this list, which is maintenance overhead. Automated tests are code, so they need to be maintained like code, right? So there may be a maintenance overhead, especially during refactoring. If your tests are not at that right level of abstraction, maybe they're too closely bound to the implementation and I'm not going to get into, you know, the kind of nitty-gritty there, but the testing, it also can decrease short-term velocity. Anybody that tells you that writing tests is free is lying to you and trying to sell something. There is an upfront investment and it is a trade-off and it tends to work pretty well, but there's certainly reasons that you might want to skimp on it, especially if you have a startup that's struggling for cash or something like that. Maybe it's a good idea, maybe it isn't, maybe you like it, maybe you don't, but that's just the way things are. So this is why you shouldn't write tests, but this workshop is not about not writing tests. This workshop is about writing tests. So we're going to go into the setup here. So there is a Git repository and I would like you to clone it. And so this is the URL. This is wrapped a bit, but the URL is github.com forward slash bone skull forward slash dead dash simple dash testing dash with dash mocha. And this contains example code for the repo. So don't mind the readme. The readme is a bit out of date, but these instructions are not. And you can use any sort of client you want to clone. This is just the command line version if you want to do that. So once you have that cloned, you're going to want to navigate into the working copy, which is the dead simple testing with mocha directory. You're going to want to run npm install. And what this is going to do is it's going to reach into a bunch of, yes, it's a monorepo. It's going to reach into a bunch of sub directories and run npm install on there. It's not really a huge deal, but I'll wait a few minutes and let you do that. And I'm going to kill by camera. I'm going to give you about three minutes. Okay, I think I'm going to move on. We kind of have a lot to cover. So once you run npm install, of course, you want to navigate into this zero one setup directory. And we're going to take a look in there. And so in this directory, there is a directory called bargs. Maybe that stands for bone skull args. I don't know. So what the bargs package is, it's a library. And the library is a command line argument parser. And so by command line arguments, we have an example here. We have dash dash foo dash dash bar equals bad. These are command line arguments. You would give things like these to any sort of command line tool. And so the bargs package will accept something like this and return a nice object for you. And let's take a look at, this is the project structure. There's, you should see a package JSON and SRC directory. And I would like you to open the bargs forward slash src forward slash index.js file in your editor. If you cannot, I will show the code here next. But it's probably going to be easier for you to just take a look at it. Okay. So this is the source of bargs. Bargs is a single function. That function is called parse. Parse accepts an array of arguments as the first option, or the first parameter, and it accepts an optional second parameter, which is an options object. This is a pretty common way of a common function signature. So it accepts an array, again, and an options object. You will see some type defs here if you're familiar with this .js.doc strings. These are actually, like, typescript style .js.doc things. And so you can look and see, you can get more information about the options allowed here. And so what this does, again, accepts an array, it's going to essentially walk through the array and start parsing arguments out of it. An argument is something that starts with a dash. It might start with two dashes. It might start with three dashes. But that is considered like an option style argument. And then anything that does not start with some dashes is considered a positional argument. And that winds up in a property, which is the underscore. And so this is a convention that other user land libraries like YARGs use. It's underscore contains positional arguments. And so you know, that's kind of the size of it. There's some special cases here, but we'll go into this in more detail, you know, the intent, what exactly it's supposed to do. But so this is the example project we're going to test, but not right away. So we're going to talk about, we're going to have you write a test now, but first we need to define some things, maybe just one thing. But it's the word assertion. So an assertion, when I talk about an assertion in JavaScript, an assertion will check a condition. So maybe that's something like a if. And you give it your expression you want to test. Depending on the result of that conditional, the assertion can throw an exception. And so this is key. The code here below, if some declared variable returning true, if it's true and returning false, if it's false, that's not an assertion. I don't know what that is, but it's it doesn't throw an exception. And that's key. So we want to navigate into this zero, this other lesson directory. So or exercise directory, whatever it's called, we want to go back up to the root of dead simple testing with mocha, we want to go into the zero to your first test directory, we want to go into the Vargas directory in there. Then I want you to make a test directory. One may already exist. So if it if it doesn't exist, go ahead and and create that, that might want to be in make dir dash p. Then I want you to create a new file in this test directory. Call it bards.spec.js, you can call it really whatever you want. But this is the file name I'm going to use here. That spec.js is just a convention. You don't have to use it. A lot of people don't. Maybe you want to call it .test.js. It doesn't matter. I don't care. But go ahead and create this file. It's going to be empty file, open it in your editor. And this the idea here is to get you comfortable with what a test is, and you will find that it is not as complicated as as you may think it's actually extremely simple. So I'll wait just a second for y'all to get that done. So say we have this empty file. I want you to put this in it. So if false throw new error, that's it. So I guess you can't very well copy and paste this. But go ahead and type that out. I can, again after this, I will try to export these slides in a form in which you can copy and paste. But for now, please just type that. And what we have here is a test. So a test contains some sort of assertion. And it's a static assertion. So false will always be false. But we have made an assertion here. Now, if false is true, what would happen? We would throw an error and the test would fail. But this is a test. This is all it is. So now we want to run this code. So I'm going to assume you've typed something out like this and we're going to go ahead and run it. So you would run it this way. You're going to go into your terminal. You're going to run node and test forward slash bargs.spec.js. If you're actually in the test directory, just run bargs.spec.js. And so what happens when you run this? So I would really expect nothing to happen at all. I would expect the code to be executed. I would expect the process to just exit. And that's it. And that's what a passing test looks like. So your mission then is to make this test fail however you see fit. So you're going to go back into the bargs.spec.js file and you're going to change maybe the conditional in there. And you're going to make it fail. And you're going to go and you're going to run it again and check your work. And I'm going to wait a couple of minutes. But if you've made it fail, the result should look something like this. Okay. So here's what I would do. I would change the false to true and that would exit it. Or that would throw the exception and make the test fail. And that's really it. There's your failing test. So we've got a passing test and a failing test. And it's extremely basic. And you could write tests this way if you wanted to. But it's not so much fun. So it works. But it's too much boilerplate. You're going to start finding that you're making the same checks over and over. I don't know if there's a name for these but I'm going to call them assertion patterns. And so you're going to find that you're making these checks again and again. So we're going to check is this value true? Is the value false? Does value x equal value y? Does the function z throw under a certain set of circumstances? And if it does, which exception? So these are some examples of common assertions that you're going to see again and again. And so that's why we get things like the assert module. So the assert module is in Node. And you're probably going to want to go check out the docs right there. That is Node.js.org. This is the built in assert module. And what it provides is assertion patterns as functions. And so we're going to use this instead of what we just did. So I want you to add something like const assert equals require assert to the top of your barge.spec.js file. And so the exercise then, well, actually, so let me talk a little bit more about the assert module. And so what that does is it provides all these assertion patterns. All the assertion patterns throw, depending on the reason, but depending on the function and why, they will throw an assertion error, which is an exception. It's just a special subclass of error. It's an assertion error. It contains some extra metadata about the failed assertion. Yeah, it's handy. So please note that we are not talking about there's a module in NPM called assert, which may be roughly the same, but we're talking about the built in assert module in Node. So you shouldn't have to go and NPM install an assert, because it's already there. So I want you to replace your assertion on this, you know, if this, where is it? This here. Replace this with a call to a single function in the assert module. And so there's going to be probably more than one function you could use in there. But yeah, give it a shot. See if you can replicate this except use the assert module to do so. And I will wait a couple minutes. And I will give you a hint here very soon. Okay, so the hint is use the okay function. So it's assert.ok. Again, there are others you could use, but I'm going to go with that one. And I'll give you another minute. So if you've used, if you've used assert.ok, it should look like this when you run it. So you'll see something like expression evaluated to a falsi value, and it'll give you a stack trace. The assertion error object will have a code property. Actual and expected. It will say, okay, so we expected true, but what we were past is false. Operator must be the operator that it uses to make the comparison. So the thing about assert.ok is it does not do a strict check. It uses the double dash. So if you say something like zero equals equals false, that would be the kind of truthiness we're talking about here. So what I came up with was this. So we've used the assert module here, and again, this is a not a strict equality check. And we've used the okay function, and we've given it false, and it should fail every single time you run it. So now we can change this test, we'll fix it, and so it passes. And this should be pretty trivial. But when it passes, what you should expect is that nothing happens. And that's, again, that's often what passing things will look like. But oh wait, just a minute. Okay, so any one of these things would work, because it's it doesn't check the strict true value, it's a truthiness check. And so you could use any of these. And this is trivial. But it illustrates the point. So this file, this barge starts back to JS, it's a test. We're testing, I don't know, the Booleans, I suppose. But it's this, these are really all tests. Are they just assertions? Anything else is kind of decorations around that. And you'll you'll have noticed that we haven't touched bars. So we're going to look at at bars here in the next section. And believe it or not, we're not even going to use mocha yet, but but so yeah, here we are. This is what we've learned so far. We've learned about assertions and JavaScript. We've learned how to run a very basic test file. And we've learned about the assert module built into node. And now we're going to write some more real looking tests. And so bars, let me talk a little bit more about bars. So this is like the motivation for bark. So the command line arguments in a node app are provided as an array, but parsing that array is tedious because you get all the dashes, you get all sorts of garbage and you need to go in there and figure out which of those mean what and maybe what you can ignore or not. And so a solution to this would be a library to accept command line arguments as an array and return an object which can be more easily understood. There are lots of packages out there that do this. Lots of them. Yards, commander, minimist. And they do it much better than bars does. So they're good packages. Bargs is intended to just be like the minimum set of things that you would possibly want for for this sort of functionality. And again, it has a single exported function which is parse. So I'm going to show you a table next of examples. So we're going to have this is what we give bargs, the parse function, this is what we pass to it, and this is what we expect back. And so this will kind of cover the use cases that we're talking about. And command line argument parsing is there's a lot going on there. But again, I'm trying to distill this down to the very minimum that we need. So here we have a table. And the first column here is the input array. So this is what we would pass the parse function. And the second column, the output is what we would expect back. And so we have these different situations that we need to handle. You'll notice that there are notes on some of these because it needs a little bit of a direction to do certain things. So you will probably want to come maybe I should just come back to it, but this is something you're going to want to look at again. I note that the output object will always have an underscore property. And if there are no positional arguments, it'll be an empty array. So you don't need to worry too much about this right now. But this is just showing, okay, this is the intent. This is what we expect to happen. We expect an option that does not have any sort of parameter after it. That's supposed to be a Boolean. So that's a Boolean flag. Yes, no. We expect that stuff that only has one dash is also a Boolean if it does not have any sort of parameter. If we use an equal sign, that means we have some sort of parameter. If we don't use an equal sign here on line one, two, three, four, five, six, we're going to need some work. You know, Bargs is going to need direction to figure out, okay, what is Baz here? Is Baz this the parameter to foo? Or is that a positional argument? It needs a hint. If we do give it something that doesn't have any dashes, that's just the positional. And we can see that on the next line. But so these are just, again, examples. Another special case is this bare double dash. And so if you've seen that before, maybe in command line scripts, everything after that double dash is supposed to be a positional argument. It's supposed to be kind of read as is. And so that's how we're going to treat this. If we pass something starting with dashes after that double dash, that's still considered a positional. And so it seems like a lot, but it's like it really is kind of the bare minimum of what you need to do. Another thing is that by default, Bargs works on process.rgv.slice2. And so process.rgv.slice2 is essentially your command line options. The first and second element of that process.rgv array you don't usually need. It's like the node executable in the script name. You just care about the stuff after it. And so it uses that by default, but you can give it an explicit array. So we want to pull Bargs into our test file. And so I want to make sure, let's see here, we should be actually in the, I believe, whichever, sorry, I didn't add this one, but it's whichever directory where it says actual tests. And so in the dead simple testing with Mocha, there's going to be several directories with numbers. You want the one that says, I think it says zero, three, actual tests. You want to go in there. And we want to add this here to the top of Bargs.spec.js. I've confused myself a little bit, but maybe we don't actually need to do that yet. So I think we want to, let's just get this one for now. I want to make sure where we are in the file system. I'm going to talk about unit tests, which is some terminology that we need to understand. So what we're going to do with Bargs is we're going to write a unit test for it. And a unit test is a test that makes, again, an assertion like in the other test, and it's going to make an assertion about a unit. But now we have to define unit. What's a unit? A unit is the smallest testable bit of code. So what is generally the smallest testable bit of code? Generally, that is a function. And for our purposes, it's a function. So a unit test is a test that makes an assertion about a unit. A unit is, in our case, a function. So what we want to do with a unit test is we want to try to test that function independently of other functions. That's kind of a broad topic as well, how to achieve this independence or isolation when testing a function. But I'm not going to go into it here. So we have a single function parse, and that's what we're going to test. But where do we start testing? Let's look at the source of Bargs and see if we can find a good place to start. And a lot of this has to do with kind of personal what you prefer to do. I like to start testing exceptions. So here, there's going to be this line highlighted in orange. And so this expects value property. This is how we tell Bargs that an option, say, dash-foo, expects some sort of parameter after it. So maybe we want to say dash-require, hypothetically, dash-require, baz, right? So we have a command and we want to pass require-baz to it. And we don't want to have that equal sign in there. And the only way to do that is to give Bargs a hint that, hey, we are expecting a value for this require option. And so in that ops, we would have this expect value array. And in that array, we would have require. And that tells Bargs, hey, require expects something after it. And so this is JavaScript. And it's kind of loosey-goosey. So we're doing a basic check to see if argv is an array. If it is not an array, we assume that we've passed it an options object. And we assume that we just want to use the default here. Actually, I think that might not even be necessary. I think that's redundant. But so, yeah, we want to actually, no, it's not. It's fine. So we want to allow a user to pass just an options object and we'll use the default process, argv slice, too. Then we go and we want to get a unique set of everything in this ops expects value because we don't really need to duplicate things, right? So we want to, and one way to do that, anyway, is to create a new set. And so on this line, we're making a new set from ops expects value. But this function is written so that if you do not pass ops, you will get a expects value of an empty array. But if you do pass it and you give it an expects value and you give it something truthy that isn't an array, parse will throw an exception because you cannot, or something that's not iterable anyway, you cannot just instantiate a new set with a number or something. It has to be something that is iterable. So what we're going to do is make a expects, let's see, we're going to make an assertion that checks our parse function for this behavior. And so let's go in here. So we want to check an exception, but before we do that, why do we want to do that? Because Vargs is a library, you're going to have people using the library, maybe. And parse is a public API. It's the interface of the function. And so when people use your library, they expect it to work. And they expect it to fail in the same way. So you might not think about it, but it's like, yeah, that's another one of the, it might not be in the explicit API, but people will expect your code to fail how it has always failed. And if we don't check our failure states, we could break that and break users and cause problems with consumers. So let's see here. We want to, I'd like to check if we are where we, where I think we are here. So let me pop out of here really quick. And I'm just looking to, yeah, so I had left, I had left out directions here. But what we want to do is come on. We want to go into the zero three actual test directory. And that directory is again a Vargs directory. There is a test directory and there is a Vargs.spec.js file in there already. And that file contains, contains this. Okay. And so we want to change this. And the first thing we want to do to it is we want to require this parse function. So at the top of this file, which is in the zero three actual tests, we want to add something like const parses require. And so it'll go and it'll grab the function out of Vargs. There are other ways to do this, but that's how we're doing here. So what we want to do is we have all these assertions. You can delete them all. And so you're going to have your test file. It will have a require assert and it will have a require of Vargs. And so what we want to do is replace all this, all this stuff with a call to assert.throws. And again, you're going to want to look at these docs. And again, it's the assert module, nojs.org. You're going to want to look at assert.throws. And that is a function that's API in the assert module and what that function is going to do. It accepts a function, accepts a function. It does not accept a return value. It accepts a function to be clear about that. And so we're going to pass it a function. And we want to pass it a function which calls parse. And we want to call parse with an ops parameter of expects value. And we want to give it a empty object for the value of expects value. And so what we want to do is we want to make sure, what we're trying to do, we want to make sure when we pass expects value with an empty object, we want to make sure our parse function throws an exception. So the test should pass but bargs should throw an exception. And so I will wait a minute while you check this out. Okay, so again, if you've done this correctly, what should happen when you run it is nothing should happen. And that is your passing test. If you did something wrong, you're probably going to get an exception. But this is what I came up with. So your bargs.spec.js should look like this now. So we're calling the throws function in the assert module. And we're giving it a lambda arrow function. And we want to pass a call. We want, in this arrow function, we want a call parse. And we want to give it options and expects value of an empty object. And so this is the thing, this is the subject of our assertion. This is what we're testing, this little arrow function. And so when we do that, we also give the assert throws.function some information about the assertion that we expect back. And so what I would expect out of this is a type error. And that will throw out of the call to new set again. So we're calling new set. And we're giving it something that's not iterable. We're going to get something like object is not iterable. That will be the message. And that error will be a type error. So what would happen if we modified this? And I would actually, I would suggest you copy what I have here. But what happens if we modify the name to read error instead of type error? What would happen, you think? It would fail. It matches the name. Exactly. We are using a regular expression here to test the message. And the message is going to be longer than object is not iterable. I don't know exactly what it says. But some portion of that message, this real expression, some portion of the message should look like this. But it doesn't need to match the message exactly. And so that's kind of handy. Especially messages and exceptions can change. It would be cool to, sorry, all the, a bunch of errors thrown out of node now have this code property, which is a better thing to check. Here we don't have that. It's just a error out of V8. It's a language error or API error. So yeah, that is our first exception assertion. We want to add another assertion now. And so I think what you should probably do is, assuming your barred spec just looks like this, copy the assertion and paste it again. And then change the value to one instead of an empty object. And we're going to write just another assertion to do this. And when you run it, it should fail. And I'm going to need you to go in and change the options to assert.throws so that the test passes. So I'm going to wait just a minute. Okay. So you have gone into here. You've changed expect value to be one. And then you've updated it. So it would look very similar to the last one that we wrote. Except the message will say, number one is not iterable. It's not going to say object is not iterable. Number one is not iterable. And so, yeah. So these are how to test exceptions coming out of our function. And the cool thing about writing tests this way is it makes you think a bit more about the API, the function API that you're writing. So while I was doing this, I was thinking, well, you know what? That's not really helpful to somebody trying to use my code. Maybe instead of letting JavaScript through that error, I should check and make sure that expects value is iterable. And if it isn't, then I'm going to throw a custom error that says something like expect value should be an array. And that would just be kind of more, I think, consumer friendly. But just something to note that writing tests like this can help you really kind of dog food, kind of feel how it feels to use an API that you're writing. And so we've done our exceptions. And I want to move on to actually checking the what happens when parse does not throw, right? So this is the usual usage of parse. We're going to give parse valid input, so an array of command line options. And then we're going to check the return value. And if you go into, you probably still have the documents open if you've looked at them at all. But if you have the documentation open for the assert module, you can poke around in there. And you'll see that there's a does not throw function. You don't want that for this. So it's four void functions, right? So if you have a function that has side effects, for example, maybe you just want to check that it doesn't throw because it's not going to ever return anything, right? But in our case, our function returns something, it should return the same, you know, it should, yeah, we want to just test the return value because we can. So what we want to do is add a third assertion. So your barge.spec.j should have these first two assertions about the exceptions. It should have a third exertion now. And we want to pass this array right here. It's just an array with one element and that element is dash dash foo and it's a string. And when you want to pass that to parse, and we don't want to pass options. And so we want to make a call to assert.deep strict equal. That's what we want to use here. And we want to assert that the return value of calling parse with this array is an object we expect. And so here it says refer to table of expected behavior. I'm going to wait just a second and make sure we have the have the instructions here. So we're going to add an assertion that asserts that passing valid arguments to parse returns an object of a certain shape. And we're going to use assert.deep strict equal to do that. And here's my refer to table of expected behavior. Does this work? Yes. Okay. So there it is. The input array again is actually this is kind of a, you know, that's misdirection a bit. So what I would do is this instead. I would go and we would check, we would pass this parse to assert.deep strict equal. And we want to expect, just give it like an empty object, right? So that would be the second parameter. And so you're going to run that code and it's going to fail. And you're going to, it's going to tell you why. Let's say, oh, well, it doesn't match. This empty object is not what parse returned. It returns something else entirely. And so you're going to want to look at that and see what it, what it returned and update your assert according, assert call accordingly. And so I'll give you a minute to look into that. But another thing is do not use deep equal or equal. Those functions are both deprecated. You want deep strict equal or strict equal. And here, especially you want deep strict equal. This is deep strict equal is for comparing objects. Strict equal is for comparing primitives. So that's why we want to use this function. So wait a minute and we're going to check that return value. Okay. So this is what I came up with for this one. So we're going to call assert deep strict equal. And we're going to pass it. We're passing it the return value of parse. We're not passing it a function. We're passing, we're actually making a call to parse and we're giving it the arrays, array we expected. And we're going to get back underscore empty array, which again is these positional arguments. And we're going to get foo is true. The foo property is true. So if we had used strict equal here, this would not work because strict equal will expect the objects to be the very same objects. And so that's not going to work because parse returns a completely new object every time. Deep strict equal instead checks primitives. And there's actually does a whole lot of things. And you can read the docs and see exactly what it means. But in our case, this is probably the correct thing to test. So great. We've written some tests in a file. It's just a bunch of assertions. And so this is not good. There are problems with doing it this way. You can overcome them. Node.js itself tests itself essentially in this manner. But what happens if you run this file and it's got these three assertions and the second assertion fails? An exception is thrown and your process exits and we never reach the third assertion. Okay. So if we want to make sure that all of our stuff gets executed, we would maybe wrap things in tri-catch. But that's adding boilerplate, right? So yeah, you're going to maybe write a function to do that for you. Then you've started writing a test framework. But there are kind of limited options for organization here. Maybe you can come up with your own set of functions to help organize things. Otherwise, you're kind of stuck using a bunch of different files. You can't put a whole bunch of stuff in one file. It's just going to be like a huge God test and it's going to be awful. So there's limited options for organizing your code unless you essentially, again, write some sort of framework. You can only run a single file at once. Maybe you have written a script to include all of your other test files. But that seems like a pain in the butt. Or, you know, it's going to need a custom script. If you want to run more than one file at once because you can't just give a whole bunch of files to Node, you give Node one script and that's what it runs. And so, limited reporting options, right? So we're throwing if something fails, but what happens on success? Again, you're not seeing anything. If all our assertions pass, the process will just exit code zero and it won't print anything. And so, if you want more information than that, you're going to have to code it up yourself. And again, that's like going down the path of a variety of test framework. And so, this is where test frameworks that have already been written can help you. And this is where Mocha can help you. So let's talk about what we've done in this actual testing section. So we've talked about BARGs, which is a library for parsing command line arguments. We've learned what a unit test is and we've learned what a unit is, which is generally a function. We've learned how to test a module. So we have a test file, we've pulled in our module BARGs and we've run some tests against it. We've learned how to use the assert module built into node to test exceptions, the behavior of our parse function and how it throws some exceptions. We've learned how to assert the return values for a given input from parse. And we've learned how this can kind of snowball a bit and this is how something like Mocha could help. So now we're going to talk about actually Mocha. So here's Mocha. Mocha is a testing framework for JavaScript. It's a framework, right? So if you think of a framework, maybe you think of, I don't know, React, Angular, Express, stuff like that. And like other frameworks, Mocha, the aim here is to allow you, the developer, to focus on your specific application specific details instead of calling into the library and making functions that way. Like other frameworks, you kind of fill in the blanks with your application-specific code, like you would write a React component. React provides this framework for four components and maybe you write a component, you kind of fill in the blank with what's unique to your code. And Mocha works the same way. It says, okay, you fill in the blanks with your tests and assertions and I'm going to give you some functions to help you do that. So again, Mocha's purpose, much like the purpose of any framework, is to make it easier to do what you're trying to do and focus on the specific concerns that you have instead of needing to worry about these things that you always need to do. Like you always need to maybe report on failures. You always need to report successes, maybe you don't always need to, but you always need to have some way to organize things. You always need to be able to run multiple files at once. So Mocha, a framework, a test framework, its purpose is to make this easier. So now we're going to go into this 04 introducing Mocha directory. And so that's right off the the main root and the simple testing with Mocha. I want to go into the 04 directory and in there there's another barge. And in this one, and I hope I haven't done this already, but we want to install Mocha in here. So you're going to go into this directory and you're going to install Mocha as a dev dependency. Typically, if you're going to use Mocha on an actual project, you're going to want to install it as a dev dependency. The only time you would not want to do that is generally if you're trying to build a plugin or something. And even then, yeah, if you're trying to build on top of Mocha, you might not want to use a dev dependency, but generally just do that. So I'll wait a sec while you go into 04 introducing Mocha and install npm install and bargs. And again, there should be a package JSON in there and it should update your package JSON when you do that. It might even make a lock file. I don't know. But yeah, please install Mocha and I'll give this a minute. All right, it shouldn't take that long. I mean, it's not that bad. So let's assume we have Mocha installed in this 04 introducing Mocha bargs. So yeah, we got it in there. And now we can open up our package.json. And in that package.json, there should be, there should not be a scripts property, but we want to add one. So we want to add a script called test. And what the value of that script should be is mocha test forward slash bargs.spec.js. And so what this is going to do is this is going to allow us to run our tests with npm just by typing npm test on the command line. And so once you've added this to package JSON, and you may need a comma, don't forget your comma somewhere, but once you've added it, exit and run npm test. And what you should see is something like zero passing if you've done this correctly. And the reason you will see zero passing is, so, oh, yeah, that test bargs.spec that has been, that should be updated to include all the assertions we wrote in the last section. So, yeah, so that does have the assertions in it, but when you run npm test, you should see zero passing. And that is because while the code in bargs.spec.js is being run is not running in a way that mocha knows about. And so it's a framework and there are conventions and API calls and things. And we're not using any of those yet. So you should see zero passing when you run npm test. So I assume this works. Again, what we want to do is add a test script to our package JSON, and we want to run mocha on our test file. So let's talk about tests in mocha. What is a test in mocha? So a test has a title and it has a body. And a body is just a function. And that function contains an assertion. Maybe it contains one or more assertions. And the title describes the behavior. So mocha's default API, it sort of mimics natural language. And if you're coming from, I don't know, Java or something, and you see this API, you're going to be like what? So it's the name of the function to create a test by default mocha is it, it. And that's a global API. So when you write a test in mocha, you don't need to require mocha or import mocha. You can if you really, really, really want to, but it's not necessary because it dumps this into the global context. And so it's called it. It makes a test. The reason behind this is so mocha is actually pretty old. I think it's about eight or nine years old now. And it was inspired by a testing package for Ruby. And that testing package is Rspec. And so Rspec's API looks a lot like mocha's API. And so for whatever reason, and I don't really know, I don't really understand the spread of things. But now, you know, in mocha, we have this kind of natural language API, Jasmine, which is maybe just as old as mocha also uses the same type of API. You're going to see it. And just as another one that can also use this API. It's kind of, you know, again, it's mimicking natural language. And that API, this natural language thing, it's based on something called BDD or behavior driven, is it behavior driven design or behavior driven development. If you're really interested, you can look it up, but it doesn't really matter because we're just talking about the API. And it does, yeah, it's, it's, I'm not going to go into BDD. But you don't really need to know anything about BDD to use mocha or any of these testing tools. You just need to understand, oh, okay, this is what the API looks like. And that's what it means. So that's what a test is in mocha. What we want to do is we want to open our barge.spec.js, which again should have all these assertions already in it. It should have three assertions. And we want to mocha-fi this test file. And so how we're going to do that is uh-oh monitor. I'm not sure if we just lost that monitor. It's got a flaky power cord. I don't know. Maybe we're still, it's still up. If it's still, if it's not up, please somebody tell me. Anyway, so let's see here. Okay, great. So right. We want to wrap the call to assert module in a test. And so it's going to look like this. So that very first assertion we made about the type error where we pass it a bear object. We're going to, we're going to do something like this. And I want you to type this out. So it should throw a type error. And this is the title. Should throw a type error. And the body is this function. And so in that function, we need to put an assertion. Okay. So you don't need to remember, you don't need to require mocha or anything. You just, you just write it. It's there when you run this with mocha. Okay. So for each of these three calls to assert, we're going to wrap it in a mocha test. And we're going to give each a title. And again, the title describes the behavior we're testing. Okay. So the first two would say something like throw a type error. And the last one should be like it returns some sort of object. But yeah, we want to wrap those assertions in a call to it. And we want to put that assertion in the second parameter, which is the the test body. And I'll wait just a second for you to wrap this up. Okay. So if you did this right, what you're going to see when you run npm test is something like this. Maybe it'll have even nicer colors. But what this is, it's the output of mocha's default reporter. And so you should see three lines, each corresponding to one of the tests. Remember that the it each corresponding to one of the tests that you've written. If you didn't get this working, maybe you'll see something like this. And I apologize for the two columns here. But so maybe the third one didn't work for whatever reason. And you're going to see something like this. So we'll see the should throw a type error, those two have passed. And we'll see the third test is failing. And this is what the output for mocha looks like if your tests are failing. So the way I got this failure was I changed the assertion to foo false instead of foo true. So yeah. But anyway, we don't we don't want to we don't want to do that. We want our tests to pass. So this is what this is what I came up with for this. And that's how I got the success. That's how I got this here. It's so we have three tests. It should throw a type error. And then there's this function, which is the body. And then that body is just the assertion we wrote before. Okay. And the second one is similar. Again, the title of a test should describe the behavior of the code under test. So the third one should return an object having property foo true. Yes, it does. But it also has this other property. And we'll get to that later. But it's good enough for now. So now we have three tests. And we run npm test. Npm test runs mocha, which loads this test file. And we're using mocha's API to create tests. So we've created three tests here. Yay. Now I want to introduce suites. So what a suite in mocha does it describes a scenario, a situation, a use case, some sort of context. There are lots of words you could describe it. It's also just kind of a logical grouping or collection of tests. You could think of it that way. But each suite, much like a test has a title string. And this is required. And it has a body function, which is also required. The body of a suite can contain one or more tests. It can contain other things too. But we're not going to get into that today. The title describes the suite, describes the scenario. And that API is called describe. So we want to create a suite and give it a title and a body. And presently in mocha, body is always synchronous. So I failed to mention before, but it was in the slide that a test in mocha can be asynchronous. It can return a promise. If you want to use node style error first callbacks, I'm sorry, you can do that too. But a suite, usually described for now, is always synchronous. That might change in the future, but you can't return a promise from a described body. So what we want to do is organize our tests using these suites. And we want to wrap the tests and suites described in this scenario. So for example, the first one might look like this. Our scenario is when ops dot expects values an object, it should throw a type error. And the assertion is the same assertion we used before. And again, the body contains a test. We can just use the test we used before. So go ahead and edit bargs dot spec dot js. And you should have three suites when you're done. And run npm test to check your work. And I'll leave this up for a second so you can kind of copy it to get started. Okay, I expect you have that copied. If you did it right, it should look like this when you're on npm test. Maybe not exactly like this, but similar to this. When blah, blah, blah is the title of the suite. And under that will be any tests in that suite. And it should be indented a bit. Wait just a minute. All right. So this is how we would make something like that happen. So this is adding suites to our tests. Our tests. So in here, you're going to have, it's pretty simple, you have describe in that describe body is your test that you had before. And you have three of these things. So you may have noticed that when we did the tests the first time, or is that so should throw a type error, and that doesn't have, that's not very helpful. What should throw a type error? What should return an object having property foo? And this is what suites help you with, because now we know what exactly we're talking about when we execute those tests. So that gives us some more context. That describes the situation of the scenario. And that's why we want to use suites. So the next core concept, and there are only really three of these things you need to worry about, there are three core concepts here in mocha. There's the test, the suite, and finally the hook. So hook may be a term that is used to mean a lot of different things, but in mocha, a hook is a code that runs before all the tests, after all the tests, before every test, or after every test. And a hook runs in the context of a suite. So just like, you can think of a hook, it's essentially just like a test. You don't actually need to make the assertion in it, but the API is the same. So the title is optional, though. You don't need to give these hook functions a title, but they can be asynchronous. They can return promises. And so you can think of these like set up or tear down functions. Maybe some other test framework or another language maybe has a notion of what's, this is how we set up our tests. This is the test harness or what have you. And then we have a tear down to clean up afterwards. And so in this API, which again, it's a global API, you don't need to require or import anything. We have four functions before, before each, after, and after each. So the idea is that before will run once, basically before any of the tests, and before each will run once for every test. And so the order in which things happen is this. And maybe I should have added a diagram or something, but when you have suites in Mocha like so, the very first thing that happens when Mocha runs this file is it goes and it finds all the suites. And that's like a depth first search. So it looks for a describe. And in there, it says, oh, okay, I have a call to it. I have a call to hook. You can nest suites. So maybe there's more described. So it looks through all the describes. Now, it doesn't run any tests yet. It doesn't run any hooks yet. It's just like mapping it out and getting an understanding of where everything goes. And so that happens first. And then within a suite, before will run. So this is the before all hook. After that, so before each will run. If these things exist, then finally a test will run, then after each will run. And then after all the tests are done in the suite, the after hook runs. Most of the time, you're probably going to be using before each, at least in my experience, before is useful maybe for if you've got something really slow that you don't want to do before every test, maybe, I don't know, start a server. I don't know. But before isn't used quite as often before each is used often. So what we want to do is we want to use hooks to make our tests a little more efficient. So that third one, so the third suite, when passed a single argument foo, as I mentioned before, it's not, yes, we're checking the value equals a thing, but we only check a single. When we described it, we only had a single property. So what I want to do is split that up. I want to split the test in the suite into two tests. So our third suite, when passed a single argument foo, it should have two functions in it or two tests. And we're going to test two separate properties of the return value here. So in both tests, we're calling parse and we're giving it dash dash foo in an array. In the first test, we're making sure that the return value has property foo and that foo is equal to true. And then the second test, we're making sure that it has a property underscore and that's an empty array. So you want to essentially copy this down and then we'll use this and I'll show you how to use hooks. So I'm going to wait a couple of minutes for you to copy this down. Okay, we're a little crunch for time here. So we want to use a hook in this exercise. So I see we've copied that down. So we want to add a before each hook to the when passed a single argument foo suite and it prepares the scenario. It sets it up for the two tests in that suite. And below, you will see a link to the Mocha docs on hooks and it talks about how to use them. But you want a before each hook and your test body. So that would be the stuff in so the stuff in it. So right here where we have assert strict equal. The only function call in that test body should be the call to the to assert. We shouldn't need to call parts in the test body. And the way I prefer to do this is to declare a variable in the suite body. And then in the hook define that variable. And there are other ways to do this. You may prefer one of the other, but I prefer to use function scope and do it this way. So I'll leave this here. And yeah, please create a before each hook that we'll call parse. And in those two functions, we're going to check that return value. And I'll forward that in a second. And you can see what the expected output will be. Okay. So if you did this, you should see the same output you did before. So we're essentially refactoring into hooks. But the test in suite output should be the same as we had before. Maybe not as we had before because we split that function into two. So yeah, that third suite is going to have two tests in it. Okay. So we need to kind of wrap this up. So this is what I would come up with to refactor the suite to use hooks. So in the suite body, I want to declare a variable. I call it result. You can call it whatever you want. But I don't define it. And so in before each, I will define the variable. And the variable is the return value of passing dash dash foo to parse. And so we can use function scope here. And because before each runs before those two tests, result will be defined by the time we enter them. And so below, we can just check the result. We can look at result.foo is this true. And then finally, we can look at the positional arguments again, which is underscore and we can assert that it is an empty array. Now remember what I said about deep strict equal and strict equal. So strict equal works on primitives. And deep strict equal works on objects. And an array is an array, but an array is also an object. And so you have to use deep strict equal if you want to compare arrays. Another way to do that maybe is we can look at the length of the underscore array. And we could use strict equal there. We could say assert strict equal, give it result dot underscore dot length, and then the expected value will be zero. So that is how to use Moka. And before each, again, we'll run before every test, after each or after every test. There's before and after. And you can again, check out the docs at Moka.js org to get more information about how to use them. And so in this section, I have covered how to install Moka, how to run tests with Moka, how to configure your package JSON to run Moka when we call npm test. And finally, how to create tests, suites and hooks in Moka and the definitions thereof. And I think that about does it. So again, my name is Chris Hiller. I'm a developer advocate at IBM, a Node.js core collaborator, Moka maintainer, OpenJS Foundation CPC. Find me on GitHub, Boneschol, Twitter, Boneschol with a zero. I also have another talk. I think it might actually be right after this one about tooling in Node.js. It's called possible tools. Might want to check that out. Also, I'm a panelist on JS Party, which is a fun JavaScript centric podcast, and they are having some sort of live podcast here at OpenJS World. Check out the schedule for that. I couldn't tell you when it is because I don't know where you live. So, yeah, time zones are hard. But thank you for attending this workshop. And I will be around for a few minutes to