Enterprise TypeScript

Setup Jest Testing

Enterprise TypeScript

Check out a free preview of the full Enterprise TypeScript course

The "Setup Jest Testing" Lesson is part of the full, Enterprise TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike installs and configures both Jest and Babel to work together for running TypeScript test files without an explicit build step. The tests are run, and any errors are fixed.


Transcript from the "Setup Jest Testing" Lesson

>> The next thing we're gonna add to our small library project is testing we're gonna have to install jest. And we'll install Babel as well. The reason we need Babel in addition to jest, is we want to be able to run TypeScript test files like test files that are TS files without having to go through an explicit build step.

Babel helps us sort of do that on the fly where we can just run Jest and Babel will take care of transpiling them into JavaScript and then Jest can run the tests and we should be all good. So we have to install a couple things in order to make this work.

We're going to run yarn Add-d jest Babel/ core Babel preset env Babel preset typescript, just making sure I got all those and I did We should see some stuff pop up here. Boom, there it goes. Again, these are new, right? This is our nice yarn Plugin for TypeScript that's bringing in those ad types packages for us automatically, it's certainly not harmful I'm kind of just using Babel here as a tool I'm not coding against it, I don't really need these types you can safely delete those, but it's nice that it sort of assumes that you want them and then you can sort of rip them out if you if you know for sure how you're using this thing and you don't need the types.

So okay, we have just we have the Bible stuff great. We next need to make a test file. So we're gonna make a folder called tests, and then we're going to create a file in that folder, Index.test.ts, and look at that, we have a file. We're gonna go back to the website cuz I've provided the tests for you.

Here it is, index.test.ts. Paste that in and hit Save. So if we want to look at these, here are some tests like a test module for the deferred class, where you know, we make sure that it creates an instance of a promise and resolves the promise when we call resolve and rejects.

The problem is when we call reject and should have the methods resolve and reject present. So now that I've put tests like that in so that it's more obvious when [LAUGH] there are failures, like I've dropped a method or something like that, and sorry, we can delete this comment at the top.

That's that's not super necessary. Yeah, we'll fix that problem in the sec. And then here's the string of my error function. We'd have a bunch of test cases for that. I'm just showing you enough of this. So, you know that it's like semi, semi real. All right, we can close that.

Now what's going on here? Yes Lint is unhappy with us. And it's saying that it was configured to run on. Test slash index test TS but the tsconfig that we're pointing to doesn't include this file, we're missing this file like the tsconfig, right? It's true, it doesn't cover anything.

There's no tests folder here. We're going to write a different tsconfig for our tests, and this error should go away. There's something we need to do in our original tsconfig, and that's add one option here. Forget where it is, so I might just add it up at the top, there it is, composite true.

So, this is a really cool option that lets us have not just multiple tsconfig files, but you sort of get pre built types and type information that can apply to subparts of the project and other subparts of the project. So you're going to see a TS build info file pop up.

And this lets you not only have differences in configuration throughout your project, but really a much more modular architecture, where you don't have to pay the full price of building your whole library. You could just rebuild the tests or rebuild the source code, and it could be nice and fast.

Yeah, so we're going to, this is called project references, right? Like, we can refer to another project. In this case, we're going to have the TypeScript, sorry, the tests area subproject refer to a reference of the outer library, and it'll work out well for us. This will become more clear once we put the other piece in place.

Now, in our tests folder, we should create a TS config, and this one's gonna be much simpler. We're going to write this one by hand. We'll just start with an empty object. The first thing we need is extends. And we're going to point to, is this right? No, it's only one set of dot dots.

So we're going to go from the tests folder back to the library's root folder. And we'll say, we extend this tsconfig.json. This means that all of the strict type checking settings we have in there. The skip lib check thing. Like all the things we set up, they will come along for the ride for free.

Now we're going to define a reference, a project reference, and this is what that looks like. So what we're saying here is that in our tests, we want to import from something as if it's a different library and the name of that thing is chat std lib and where we can find the code for those imports is in that source folder.

So this is kind of cool because it lets us write our tests. In a very similar way to the way you'd like externally refer to this to this library instead of having to go like dot dot dot dot dot dot all the way out and then sort of go into the source folder So that's cool all right a couple compiler options we have to define here.

And the first one is types. We want to make sure that we allow just types in here. And the second one is rootdir. We're going to say that dot dot. Here's the reason. Are we getting the problem here? Actually, maybe that's correct. We'll see where we're going here.

It is, I should have it like this. Sorry, for the confusion there. This is not pointing to the source folder. This is pointing to where we can find the package JSON and the root of the project, the package manifest. We're going to use the main field in our package JSON here.

This will be where we're getting our code from. The point of this, what we're trying to accomplish, Going back to this tsconfig, we just want to refer to this the same way a consumer of this library would refer to this. All right, rootdir. This needs to be the parent folder, and that's because the root directory has to contain all the TypeScript files, like the source files that we're kind of compiling against.

And we're gonna be referring to stuff in that source folder, I mean through the reference here. But we want sort of the outer project to be considered the rooter. And then finally we need to include. And that's just gonna be dot everything in the test folder is included like and what that means is this TS config applies to this folder all of its sub folders, alright one more thing we need to do before the test and that's take care of Babel so we're going to do.

Make a file in the root of our project called .babelrc. And what we're trying to accomplish here is we want Babel to take care of converting that TypeScript to JavaScript behind the scenes so we don't have a separate building our test step, which is kind of annoying. We can just have these TS files to just run in jest we set this up once we forget about it, and it should all still work.

So I have a babelrc file now, I'm gonna go back to the course website so I can get the contents of that file. It's right here. So we're just bringing in some presets here. So we're saying, we're targeting note 18. So that's gonna affect how Babel transforms our code.

It's gonna make sure that if we have any language features that we use, which are not available in node 18, it'll compile those down and, introduce polyfills where it has to. And then this preset here is what tells Babel to convert the TypeScript to JavaScript behind the scenes, you won't see the JS files.

It's just happening to your benefit automatically. And this is it, we're ready to run yarn test now. And look at that,we have some tests running. This is a good lesson here of auto-formatting. So we can see that, okay, the tests are running. Some of them are passing. These two are failing.

And in both cases, there's a big little piece of white space preceding those, those lines of text. So whenever I see something like this, the first thing I think of is has something added space in a tagged template string, thinking it can safely indent, you know a word and in doing so it's like added spaces to the string So, let's go check for that first in our tests, and we can, I'll look at this in a sec.

>> I don't know if it's formatted differently for you. I had a couple of leading white spaces inside of stringify error value.
>> That's probably it. Thanks for catching that. Yeah, it's folded, I didn't see it. There it is, that's it . It's this right here It's outside of the dollar sign.

That is our culprit And they'll all pass now, nope, they won't we have to build it and then it'll pass Great, okay, so clearly our tests are working effectively. We check down the bug, we fix it. So we had a question in chat asking us, why do we need this babelrc?

If we were to say we want to write our tests in JavaScript. Like Jest understands these JavaScript files, right? If you look in Jest's documentation, it'll tell you, name of testfile.test.js. Jest does not understand files, like tests that are written in TypeScript itself. Now you have a different option than what we set up here If you could have a separate step where you'd create, I don't know a folder called tests dash build, and you could have TypeScript, compile your tests, and then produce the resultant JavaScript jest tests, which just would be happy to run for you.

This is an alternative approach where we're using Babel to sort of Transform the TypeScript into the JavaScript just once on the fly behind the scenes, you don't have to think about it. You don't have stuff in your folder where you have to worry about like, I just changed my tests.

I have to build them again, so that I can then run them. You could write scripts around this and stuff but like, what you're effectively doing there is the same thing that Babel is doing for you. It's making sure that you can write, just like, you know, the reason you use Babel for library code or app code, you can use modern language features, maybe even things that Node 18 doesn't support yet.

You can use TypeScript, and then Babel is doing the job of taking that You know, doing the type checking, emitting JavaScript behind the scenes can think of it maybe maybe it's in a temp folder somewhere not not really important where it is. Then just sucks that in and it's the JavaScript that just runs it's actually not an attempt folder.

Jest knows how to talk to Babel, and so it's just sort of part of an in-memory, you know, build pipeline. So by the time Jest is running a test, it knows it's not like loading a file from disk. It's taking the output from Babel, and it's doing that.

And then why we had babelrc here? Babel can be used for a lot of things. We're using it for two specific things here. One is down leveling our code so that anything that we would write which is only supported in node 20 or node 20, yeah, like 21 or whatever it is, it's all sort of simplified down to what node 18 could handle.

An example of this would be if we used private static fields like hashtag fields, that's an ES 2022 feature. I don't believe Node 18 supports that. So this will take care of compiling that down into something runnable and then this is telling Babel, all right, after you've compiled things down.

Also strip away the types, like do the type checking and then strip away the types and you should be good. And Rugmat is asking on Twitch, does Yarn just automatically run Babel or how does that work? It's Jest that knows how to interact with Babel. If Jest finds that Babel is present in the project And that there's a .babelrc file, it will call into Babel like it knows how to do that.

But it relies on Babel to do the TypeScript sort of inline compile on the fly. Yeah, and so another question we got, we just we just had to do a build to make sure our tests pass. This is proof, that references kind of operate the way the way I said they do, which means we're pointing to a package JSON effectively like a folder in which you can find a package JSON.

So the fact that we needed to build it means that when we're referring to when we're running our tests, we're pointing at the dist folder. We're not pointing at our source code TypeScript, we're loading things from the dist folder and we're benefiting from these declaration files here. This is our type information.

I wonder if this illusion is not too clever here. Now, it knows the source code is there. It must be in this, like, tsbuildinfo file, where this is a bunch of hashed files and things like that. So, yeah, it's probably mapping through this, kind of like the way a source map would.

To find that like index.ts is ultimately where this comes from because this stuff in the build folder came from index.ts. But like if we were to delete stuff from the dist folder, our tests would not be happy. That's where they're actually running code. Which is cool because like we're actually running our public API surface not a bunch of stuff you know that's internally in specific files it keeps us you know this is great for like integration testing where you really want to get an accurate representation of like What is this library look like what does it look like from the outside world what's its contract with the outside world and that's what we're testing against here

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