Video details

JavaScript Tests in Node, the Browser, and CI - Rob Richardson


audio español - This no-slides talk demos creating tests in JavaScript. We live-code sync tests, async with callbacks, promise-based tests, and async/await tests. We craft tests in Node, tests in the Browser, and unit tests that run from CI. This can be a great tool in your toolchest of unit testing, integration testing, end-to-end testing, component testing, etc.


Hi, welcome to JS. Comp Mexico. Today we're going to take a look at JavaScript tests and Node, the browser NCI. Now here's the part where I tell you I'm definitely going to post the slides on my site tonight, but in this case there are no slides. This is it. Now the code that we're going to look at is online. Right now we can head off to GitHub and here's that GitHub repository. Now here we have a Done folder and a Start folder that allows us to begin. And then you can compare your answer with mine to see what I need to fix. Now you can get to this GitHub repository really easily from Let's click on, click on Presentations here's, JavaScript test and note the browser and CI. The code is online right now. While we're [email protected], let's click on About Me and we'll see some of the things that I've done recently. I'm a Microsoft MVP, a friend of Redgate and the Docker Captain. I'm also a Cyril developer advocate. If you're struggling with your data mesh, I would love to learn from you. Az Givecamp is really fun. Az Givecamp brings volunteer developers together with charities to build free software. We start building software Friday after work Sunday afternoon. We deliver completed software to the charities. Sleep is optional. Caffeine provided. If you're in Phoenix, come join us for the next AZ Give Camp. Or if you'd like a Give camp here in Mexico or wherever you're connecting from, hit me up on email or Twitter and let's get a Give camp in your neighborhood too. Some of the other things that I've done I worked on Gulp as a core contributor in version two and version three and I replied to a Net Rocks podcast episode. They read my comment on the air. They sent me a mug. So there's my claim to Fame. So let's dig in. Now I've opened the Start folder here inside vs. Code and let's create a new file Hello World JS and let's get time to first it should pass. Now with any it function we will have an assert true reason. Now we do need to import assert. So let's import assert from assert and that is our first test. Let's run it CD 21 NPM test and we have one passing test. Excellent. Now I did do a few things behind the scenes, but our goal was time to first it. We got there really quickly here in our package. Json. I did install Mocha and Chai so that we have those available. Mocha is our test runner and Chi is our assertion library so that we can validate that our results are as we expect. And when we do NPM test, I want it to run Mocha so we have our test and we can assert something. In this case, I just asserted true and give it a reason that will display when it fails. We can name our test and we can also group them. So let's start by testing this add function. Now in this case the ad function is pretty straightforward, but it'll help us build up to some really robust tests. So I'm going to create a new file here in the test folder. I'll call this testadd JS and let's import Chi from Chi and we'll import the add library from up one directory add. Now that we have our libraries imported, let's describe our test and this is that grouping. And so in this case it will be the add function and we can give it a scope. So anything inside this describe block will be in the add group. Okay, now let's say it should add two plus two. Well, two plus two, I could accidentally multiply. So I'm going to say two plus three. Now let's give it a test body. And here in our test body we can do the standard pattern. So we'll do arrange act and assert. Now what do we want to do? We want to act and we want to say Const actual equals add X and y. Okay, so let's say Const X equals two. Const y equals three. Now we've set up our expectations and we've accomplished our task. Let's validate the result expected. So we need another expected variable. Const expected equals five. And now we've got our test. This arranged act assert pattern is really helpful in testing. We'll set up our expectations, we'll accomplish a task, and then we'll validate that the result is as expected. Now in JavaScript we can also concatenate strings with plus. So let's do that should add a and B. So X is a and y is B and we expect this to be AB. And now let's run our test again NPM test and we should get our Hello World test in addition to our ad imported from module not found. Oh, I bet I didn't import this correctly. Add JS. There we go. There. So now we've got two passing tests and a nice fail. Now let's validate that we can get a failing test. I'm going to say ABC and we'll be able to see not only our passing test but also our failing test. And the line number that it failed, what we expected and what we actually got. And that's why I like the Chi library is because it's really clear what the actual and the expected are. With assert we can just say assert actual equals expected. But now we don't know what is the actual and what is expected. That's why I really like the try library. So let's make this test pass and we can validate that our test is as we expect. Perfect. Now like any good developer, let's copy this and let's create a subtract test test subtract JS and in subtraction we don't do this, but we can say should subtract two from three. So in this case X is three, Y is two, and we expect this to be one. Let's run our test and see how it works. Importing a few things we oh yes, I should import expect from Chi, not just Chi. So let's fix up this test as well. There we go. Now that we've got our subtract test, let's run NPM test and we'll see what we got. Unexpected token semicolon on line. Oh expected one. There we go. Npm test and we've got our passing test. Oh callback is not a function. Let's dig in. What does subtract do differently? Well in this case subtract uses callbacks. So let's see if we can test a callback. Now in this case we will say error and actual and we'll move our assert here into this. And now we've got our test. Except for how do we know when we're done? Well, Mocha actually provides a really good way to say when we're done we can just when we call this we will be done. We can also expect error to equal null just to validate that it is as expected. And now our test will pass. Excellent. Now what if we don't call done? Now this is an asynchronous test that does not complete after 2 seconds. Mocha will timeout and if we need to we can adjust that timeout value. So we've called done and everything works. Great. So there's a callback based test. Now in the case of multiply we have promises. So let's build a promise based test testmultiply JS and so let's grab our subtract test and set it in place. Here we'll swap subtract for multiply and let's adjust this should multiply two and three. So I'm going to make this two and this three and we expect this to be six. That's pretty straightforward. But now we have a promise based test. So I'm going to say instead of actual here. Oh it looks like we might have messed up our subtract test too. We don't need this actual now I'm going to test this result. And now instead of doing this I will say then and I will pass in the actual value this way. Now this function that I call when I get my then can now do the expected content. I no longer have an error that I can do. But now I can also still call done when I'm done. Here let's run this test and validate it works as expected. Yes. Excellent. Now this test was for promises, but we can also instead of passing in done and calling it, we can just return a promise to Mocha and when that promise resolves it knows that it's done and by comparison throw new error fail. If we have a failing test we can call NPM test and we'll get that error as well. Now that's excellent. So how would we test then? Let's modify these parameters. How would we test async and await? Well in a similar way that we can return a promise we can await. So let's have this async function here we'll say three times four is twelve. And now we can say Const actual equals await multiply and we'll do our standard assert here and because it's an async function then that will work just fine. Let's run this and we get great results. Our divide test is synchronous. So let's come back to add and we'll copy this and we'll say test divide and we can paste this into place. Add becomes divide and we can divide two over two and that will be one. Now there is another case where should divide by zero can be a thing. So let's say X is two and y is zero. Now in this case we expect an exception. So let's do that. Let's say let's try and catch here and we can say we expect this to be can't divide by zero. And here we'll say actual equals error message. So we're not actually grabbing the results here. But we will have to say let actual and now we can validate that. That works as expected. Well even better, let's take a look at the code. It returns. So let's say the code is 12345 and now we can run this test. Now we run this test OOH we expected undefined to equal 12345. It sounds like we didn't throw our exception. Let's take a look at our divide code and it looks like someone was helpful and decided the math worked differently. No, let's not do that. Let's say constant divide by zero and we will say error code is 12345 and throw error. Okay. Now with that in place, that was great. We were able to use our test to be able to discover the sharp parts of our code. So we created an async and await test. We created a promise based test, we created a callback test and we created a synchronous test. We use tests to discover our libraries to validate our libraries to even find edge cases that we wanted to prove, including adding strings and dividing by zero. Now how would we add this into node? Well we've done it in node. How would we add this into a browser? Well the cool part here is that we can do Mocha init no. Npx. Mocha and I'll give it a folder and we're ready to go. Now that we've done this init, it's going to spit out a few files. We have this index. Html that will call into Mocha and call into our test. Okay, let's also add nodemoduleschai. Js. There we go. So now we have Chai included in our solution. And now let's go add all of our content. We'll have add JS and subtract JS and multiply JS and divide. And we'll also include the test variance of those as well. So let's say test add and test subtract and test multiply and test divide. Let's go grab those files that we just built and set them into place. Now in this case I don't have any build system I'm just including the content here in my page. But I do need to adjust these ever so slightly. Now, previously we just said import. Now they're included in the page, so we don't need to do that. But we will say constpect equals Chi. Expect cool. We'll do that inside of each function knowing that the browser has already imported these and we'll be ready to go. Now, the cool part is we didn't really need to change the logic of our test. It's still just JavaScript. We just need to update the way that we import it and we don't need this one at all. Okay, so now we have this index HTML page. Let's load that in a browser here and take a look at our tests. Now, I love this page because it runs each of the tests. We can drill into a particular group and drill even into a test and it'll show us the details of that test. Now, in this case, we have a failure should divide by zero. Well, it looks like we're doing the correct result, but let's take a look at this divide code. And yes, it looks like we still have that same bug. Let's come back and grab the bug fix here for our divide code and we'll put that into place. Divide and we've got passing test. Now. Now the cool part about this is it's just a browser. So if I open the Chrome developer tools, I can take a look at the sources. And if I want to, for example, debug this divide test, I can set a Breakpoint right here. I can refresh my page and I can step into and over my test. So let's watch it throw the error there and we'll catch it and we can see that we've got the results. Now probably I didn't do it in time. So yes, the done timeout failed there. But we can see how we can debug through our tests in a really elegant way here. Mocha in a page is great. So now how do we implement this inside of continuous integration in a DevOps pipeline? Hey, DevOps person, can you just open this page and see how it works? Yeah. No, that's where we reach for Karma. So let's take a look inside this folder. Now, I've already installed Mocha and Chai, so let's say NPX Karma in it. Now, I like Karma in that. It kind of interviews me and lets me know what details I need for my thing. So I'm going to say I'm using the Mocha framework. Do I want required JS? No, I don't. What browsers would I like to use? Chrome is great and I could choose others if I wanted to as well. In this case, I'll just choose Chrome. Where are my test files? Star. Js. And what do I want to ignore? Node modules. And now I've got the content that I want to do. I want Karma to rerun on any test change. Yes, I do. And it dumps out the content into this Karma config. Basically just the results of all of those questions. So here's that Karma config. And I do want to include the Chai library there as well. But I could adjust the answers that I gave there. The Progress reporter is great. Here's the Port that it will run on the log level. Auto watch is set to true. I could adjust the browsers if I wanted to include more and it will run forever. Perfect. Now, I did add inside package. Json to be able to run Karma, but let's just run it from the command line. So I will say NPX Karma. We first need to set our test files into place. So let's go grab all of these test files and we'll set those in place. Now that we've got all of our libraries and test files, NPX Karma start. And now it will kick off our tests. Now, the cool part about this is it's going to launch. So I went offline and I found the error. I had excluded Node modules, which excluded Mocha and Chai, which meant that I couldn't get access to my test. Let's try this again. And now we see that it opens up a new version of Chrome. Now, this version of Chrome over here is not my standard version of Chrome. It launches in a different version of Chrome so that I don't have any of my browser plugins installed or any of my cookies that might interfere with my tests. Now, these are unit tests. They're not integration tests. Users aren't clicking through buttons, but we could still debug our tests and take a look at it. But we can see that we've got our tests and most of them passed. But we still divide by zero here. That's not great. Let's come in here to our divide and take a look at it. Oh, yes, we've still got that bug. Let's pop open, divide. Js and we'll go grab our fix and set it in place here. Now, because Karma is doing a watch, it will automatically rerun those tests. I'll come back into my console and I see I now have successful tests. That's perfect. Now, in this case, we did an NPX. Karma start. So we're continuously running. But we could also override this in a package JSON. So here in my package. Json, I say NPM test is Karma Start single run and I want to run in these two browsers, Chrome Headless and Firefox headless. So let's do that NPM test. And now I'm running all of my unit tests inside each browser. In this case, we had a bunch of tests and we see that they executed correctly in both browsers. We have 16 successful tests. This was excellent. We were able to run our tests inside of node, inside of a browser where we could debug it and inside of Karma so that we could get it inside of an integration test. We saw how we could do synchronous tests, callback based tests, asynchronous tests with promises and asynchronous weight tests. We saw how we could use tests to be able to validate our code, discover problems in our code and even flex the edge cases associated with our code. The code that we just built is up online in this GitHub repository and you can get to it really easily from I'll be in that spot where the conference is designated for live Q and thank you for joining us for JavaScript tests in node the browser in CI.