Check out a free preview of the full Testing and Modular Front-End course

The "Making a Test Suite" Lesson is part of the full, Testing and Modular Front-End course featured in this preview video. Here's what you'd learn in this lesson:

After reviewing a tape test function, James demonstrates how to make a test suite out of multiple test functions. James takes questions from students.


Transcript from the "Making a Test Suite" Lesson

>> I can just go over how this test file works a little bit, just to cover all the pieces so that everybody's got the concepts down. And the first thing we do is, we require one of these testing libraries. They have pretty much the same API, so it doesn't really matter which one we load.

So here I'll use tape, then we call the test function that we get from tape, with name if we want but the name is optional, so that's fine too. And then inside of the test function, as the second argument, we have this function and we get a handle to this t object.

The t has a bunch of methods that you're gonna also find on the node cert package, but it has a few more, like t.plan and t.end. And plan an end are how you can specify when your test is over with, because otherwise JavaScript has this thing known as run-to-completion semantics.

So these three things are gonna fire and then this test function is gonna be over with, so we kind of need a way to tell the testing library that hang on a sec, this test actually needs a bit more time. It's gonna do more stuff asynchronously before we move to the next test.

So this sort of this implicit flow control between every time you run tests. So I also wanna show you what it looks like to have a test suite. So maybe instead of putting both of these tests into the same file, we can put this into another one called second.js.

So I'll delete it from the first one and I'll move that test.js file, call it one.js. And the code should be on the GIT repo, by the way, because of that bash loop that pushed it, so get it while you can. So now I've got one and second.js.

I need to modify second so that I require the testing library as well, so I'll do require('tape'). Now we can individually run either script, which is kind of unique to, not very many testing libraries let you run your tests like this. A lot of them have you run a special program, because they depend on things like globals and they have a lot of setup involved.

But I really like tap and tape because you can run your test with node if you like. However, if you have a whole lot of files that you want to run one after the other, what you can do is, if you install tap and tape globally by doing this command, then you'll get a tap and a tape command.

So if I do something like tape *.js, it's gonna run all of those files that match that wildcard. The same is true for the tap command, although it formats things a little bit differently. The tap command is still parsing the tap format, but what it shows you, oops, sorry, I guess I didn't have that one.

But what the tap command is gonna show you is a more concise output, so. Actually, I have that in my cache, so this will make it a lot faster. This alias does npn install with a --cash -min Infinity, capital I, in that only reads things from my local cache.

But it's still slow because I've got so many packages in my dependency graph that it really struggles to cope with that. I do wanna show what the tap output looks like, so I'm gonna wait. Hopefully, not very long. Well, we still have Internet, question.
>> Did you say that was an alias, or is that existing-

>> Yeah, it's an alias. So I'll show you what it, that's what it is. If you too want to be able to use npm offline in version 5, there's a lot of stuff coming down to make a lot of these offline use cases a lot smoother. There's gonna be a flag called --offline, and some other tricks that you can sort of customize how you want your offline behavior to work.

There's one flag, I think, that's coming, that will have you prefer to use your local cache if you have the latest up-to-date package. Like that. Okay, so there's also the tap command. So here's what the tape output looks like. It pretty much just concatenates the tap output and keeps the plan around so that you see it at the end.

Tape is actually not doing anything special, I can show you the entire program. It's more complicated than it used to be, but it used to be pretty much just, it will loop over all of the files and require them. So I can show you what. Pretty much what it's doing is, if I did fs.readdirSync, just to make something easy, ('.').forEach.

Actually, if I .filter that for things that end in JavaScript, .js, .test(s), regex's really handy. And then you can, for each of those files, I want to just require the file. Oops, and then that's gotta be a absolute or relative path. So, oops, received undefined. Path.resolve, sorry. Okay, so we do something like that.

And there we go, there's our test output, pretty cool. And then when you finally exit by doing Ctrl+D in the repl, that's when the testing library knows that, the program is finished. So I can print out the rest of the output, which is the plan, and this other stuff at the end.

That's just kind of a convention in tap, it's not really specified, I don't think, in the protocol. And then how tap formats that output is a little bit more terse. So it just sort of shows you a summary for every file, and there's different ways to customize the output with tap but hat's the default one that you get.

What's cool is that even though I'm using tape in this test suite, because I'm printing tap-formatted output to standard out, then the tap come in, that's all that it's gonna read. So it's not like it's tightly coupled or anything. Question?
>> Why does tap have a time on second js but not in one js?

>> I don't know. [LAUGH] It's just how it is, I guess. Yeah, tap does a couple of other fancy things. If some of your tests are taking kind of a long time, it'll print out the, this took 3000 milliseconds-
>> Outliers.
>> Or whatever, yeah. It also prints the summary time.

So the tape command is much simpler. Most of that code in that command is just to work on Windows with globs, because they don't work very reliably if you're not on a Unixy system. Okay, so now one other thing that I wanted to cover, so I covered kind of how you can split up your code into a suite.

So what I would typically do if I'm gonna organize a real project is I typically have a test directory and I put all my test files in there, then I'd have something like my implementation file. So this would be, I'll just have a really simple placeholder function, it could be asynchronous though.

It's gonna asynchronously compute with set timeout, maybe half a second. It's gonna compute with a callback n times 11, so, actually 111. So this is just a really simple module just to kind of give you an idea for what a slightly more realistic but still plenty contrived test suite is gonna look like.

So here I have one.js, I would probably load in that,- Call it elevenizer, cuz 11 plus an extra 1, izes it. I don't know, it's a silly name. I'm in the test directory so I can require the parent directory like that, and then I will do things like, this could be called single digits.

So everything from 0 through 9, maybe, so that's gonna be 10 tests. I could have a little for loop or I could kind of explicitly enumerate them. So I'm gonna something like t.equal(elevenizer(0) should be 0, 1 should be 1, or I can have a little loop. And then I can just assert that, well, I guess this would sort of defeat the point cuz I'm doing the same arithmetic, so why don't I just list those out?

So 0 should be 0, 1 should be something else. I don't have to do all of them, I'll skip a few. There we go. So that's not gonna be ten tests, that's gonna be six. I'm gonna have these values, are what I expect. It can run the test.

So there we go. It doesn't actually work because my test, my implementation, doesn't return the value, it's actually a callback. So I have to rewrite all of this. And then, if you have errors, you can either do t.ifError, like we saw with assert. Or there's an even shorter alias, you can just do t.error, which also does ifError.

So what this is gonna look like is something more like this. Right, so, and then maybe for the description I could put what the value for the input is. So we do this for a couple of other values. Maybe, Put 7s, 7s are cool. That's gonna be 6.

So if I run that test now, cool. So we're testing kind of like a real but still contrived API example. This is a really super common pattern.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now