1. 0:00 - Intro 2. 2:00 - Getting started & Setup 3. 5:10 - Creating test file for 'products' 4. 8:06 - Adding test command and running it 5. 9:24 - Refactoring in order to implement supertest 6. 15:50 - mongodb-memory-server (Author recommends mocking the service instead, this is demonstrated in chapter 10. ) 7. 18:47 - Adding more tests 8. 25:22 - JWT 9. 29:54 - .skip and .only flags for jest 10. 30:54 - Test file for 'Users' (The authors recommended way: mocking out service instead of mongodb-memory-server) 11. 36:26 - Mocking out and 'spying on' userService 12. 41:27 - Running the first User test 13. 43:57 - Clearing mocks in between tests 14. 46:05 - Reseting mocks in between tests 15. 47:16 - Test creation of user access token (Last test in the tutorial) 16. 52:34 - Bonus: Jest extension for VSCode
I'm really greatful for such tutorial. Before knowing about all taht stuuf, I asked myself : "How cna I test my API efficiently, with the most detailed tests possible and reproductibility ?". I almost started to cre&te tests using bash & curl, and starting my server as a daemon. I knew it was not the solution, so I didn't started yet. Thanks to your video, I know the correct way now.
This video is exacly what I need ATM. ChaiHttp just decided to stop working, also it have problems with ES6 import modules, so I really needed to learn how to use Supertest, also I learned a lot of good practices in this video. Dude, this video is a complete GOLD.
Thank you very much, this is exactly what I need for my practice :> There are extremely few videos about Jest comparing to Cypess. Luckily I found yours :>
This channel is a gem! I appreciate that you cover important and practical topics (i.e testing, TS) in great detail. I've seen some videos on the 2 topics mentioned above but not as as it pertains to Nodejs. Thank you and you definitely deserve more views/subscribers.
Ooooo man. Don't miss even 10 seconds of this video (13:30 - 13:40). I missed the movement of `routes(app);` from ./app.ts to ./utils/server.ts and for 90 minutes was questioning my sanity as the API worked but the tests that exercised the route didn't. Because without this step, you get a 404 error by default for calling a route that doesn't exist - exactly the response code needed to pass the first product unit test - and the second unit test will never pass whatever you do.
Hi Tom! Your authentication API video with typegoose and zod helped me tremendously. I know i will learn a lot from this, so Thank you for making this video!
I really like your use of multiple describe blocks here. I recently implemented something very similar for a project and have found having these tests is really helpful when refactoring. Thanks for the content!
Thank you :) I was a bit nervous about doing that in this tutorial because I didn't want to turn anyone off, but it's how I structure my tests and I find it really valuable.
Great testing video. Appreciate your time and effort with it. Found your channel a few weeks ago and have slowly been making my way through all the videos. :)
Hi Tom, thanks for the video. With the updated repo, if you follow along to the video you will find that you will not be able to get most of the mongodb-memory-server section to work. 1. You will not be able to get product.productId after calling createProduct because productId doesn't exist in either the ProductDocument or ProductInput interface. To fix this I had to put productId: String in the ProductDocument interface, not sure if that is the right approach but the tests passed after that 2. You will not be able to use signJwt because it expects 2 parameters now, not sure how to get around this one. I skimmed through the rest of the video and didn't see any fixes for that so I stopped trying here.
For current versions of jsonwebtoken error `Error: secretOrPrivateKey must be an asymmetric key when using ES256` could be solved by adding `allowInsecureKeySizes: true,` option to signJwt function. Cheers.
Hello, thanks a lot for this tuto! I have a problem with using the send method for a post/put request, I always get an error "Right-hand side of 'instanceof' is not callable" What is the reason of this error?
Late comment but for some reason Jest times out when i'm running all of the test after I included the "should return a 200 and create the product"... but if I just run that test individually with '.only' then it doesnt time out and passes. I even tried extending the time but it still times out when running all of the tests.
Which extension are you using to get "intellisense"-isc behaviour in the terminal? And also which extension to get the "Run | Debug" options over each "Describe"-block?
because the service function that we are mocking are async and they return a Promise, and in jest we are saying that it will return a Product. So typescript is saying , that the return type is not the correct one.
Nice video. I got a problem with this, inside config folder, test.ts file have the private key, if I remove the private key from test.ts file config and create the custom-environment-variables.ts file to read the variable from an env file, it doesn't work because can't read the private key value, if I just paste the private key into the test.ts file all work again, any idea or config I am missing there?
If your problem is that it can't get the value when you're running the tests, it's because you haven't called dotenv.config() before running the tests. You should be able to add it to a script that runs before your tests run. Otherwise, just generate some public and private keys that you use for tests and put them in test.ts
@@TomDoesTech oh yeah, that was my problem, the dotenv.config() call, I used that on a file that call the one I was testing, but not on the actual file. Thanks
Hi Tom, first of all thank you for your dedication for wanting to help others. I wanted to ask you how would I do the same with testing the endpoint only, without controllers and services?
@@TomDoesTech The endpoint has the logic inside, it's not split with controllers, so the logic is declared on the POST request (e.g signing up a user with jwt middleware) and returning a token. Its all inside the routes folder.
@@arberi99 Do it exactly the same as I did it in the video. Tests should care about implementation, all they should care about is that given X input, Y should happen or Y should be returned, how your function does that shouldn't have anything to do with the test. Having said that, split your code up a bit and it will make your life easier. Check the video before this one to see how I did it: th-cam.com/video/BWUi6BS9T5Y/w-d-xo.html
This is awesome!!! thank you for this lesson. One request, can we make it a full stack app i.e. can we build a frontend to consume this API? thank you once again.
Good content, but it's honestly really hard to watch, with everything jumping around on the screen. So please consider this, because it's really not easy on the eyes to consume your content, and it's hard to stay focused. I haven't noticed that in your other videos though as much, but this one is kinda tough I still hit that like button though, because there's still value in the video
Hey Mohamed, thank you for your kind comments. The video you're after is here: th-cam.com/video/oSz23pPBpFY/w-d-xo.html Then you may want to add Google OAuth: th-cam.com/video/Qt3KJZ2kQk0/w-d-xo.html
Read the docs if the explanation wasn't clear enough. Supertest will start an instance of your application and run requests against it. Axios will just send a request to a given URL.
I didn't enjoy this video as much as the last one the explanations were too brief it felt like you were just rushing true it also I did not really understand that last test so well. i don't have much experience in testing so maybe that is the problem. looking forward to the next video in the series. Thank you Tom.
Thanks very much, great video. When testing 'user registration' for 'given username and password are valid', I kept getting a 'TypeError' => 'user.toJson is not a function' in the referencing user.controller.. I removed the toJson() from the omit function and test passed and the route still works without. Couldn't quite understand what the issue was? Testing removing toJson from the omit functions in user.service, the app broke on the spot.. tried JSON.stringify instead, he wasn't having it.. Anybody hat a similar problem? Tks, regards PS: just seeing now that in the controller the createUser() is called from where the user is returned through the omit function already... so in the controller I only return and send the user
Are these tests considered unit-tests or integration tests? I'm leaning towards unit-tests since there is not really a database interaction and you are only mocking a service. But what if in the service you make a call to the database and fetch some data, is it considered to be an integration test then?
You could argue for both, they test across multiple units of code, but I'd they say are unit tests. Don't get too wrapped up in what a test is called, be more concerned with how it makes your software more reliable.
@@TomDoesTech thats what I’m thinking as well, but for my assignment I need to get the correct definition. If I describe it like that I think it will be sufficient enough. Thanks for the vid 👍
@@TomDoesTech While I do find this a confusing subject when it comes to end points and especially databases, it's to my understanding that if one were to mock the database or API in any fashion that means disconnecting the reliance on another component to be functioning properly in order for a test to pass, it's a unit test. The reverse being an integration test when the code tests multiple aspects of the program at the same time; i.e. we write a test that connects to express which in turn runs a function that connects to a database and creates or modifies or deletes a user for example. The only confusion I have there is when we also consider end-to-end or end point testing which more or less involves connecting to say a page route, but does not involve connection to a database or other component? I find this especially confusing when using a template engine like .ejs where even a basic rout to get a page might also be required to render dynamic data. I suppose I could just write the tests to ignore the dynamic data aspect, focusing only on reaching status 200 or 302 for rerouting. On a different note, I found in my current project that jest loved to assume the server was open even when it should be closing since I separated my server listen from the app just like you did here with server.js - importing the server separately. let serverListen // teardown beforeEach(() => { serverListen = require("../server.js"); }); afterEach(() => { serverListen.close(); }); I found this little bit of code resolved the warning. I was running quite a few tests concurrently and solved those by changing serverListen = require("../server.js") to a specific port for each test file serverListen = app.listen("5006"). This was NOT always necessary, I just found it solved issues where one of my test suites would "butt heads" with another on and off. Great video overall. Good mocking content is actually tough to find in Javascript and since I am new, there's an amazon aws-sdk mocking issue driving me nuts atm hahaha. (I REALLY need to learn typescript and c#) >_
● Validation Error: Preset ts-jest not found. Configuration Documentation: jestjs.io/docs/configuration if ANYONE face this error, remove the present in jest.config.js file
Testing against a database is a nightmare to manage in CI pipelines. If you think it's not testing enough, then test against a database. I'm usually pretty happy with the test coverage doing it this way.
I keep getting a 403 on this test describe("given the user is logged in ", () => { it("should return a 200 and create product", async () => { const jwt = signJwt(userPayload); const { statusCode, body } = await supertest(app) .post("/api/products") .set("Authorization", `Bearer ${jwt}`) .send(productPayload); expect(statusCode).toBe(200); expect(body).toEqual({}); }); }); everything works 100% on postman
1. 0:00 - Intro
2. 2:00 - Getting started & Setup
3. 5:10 - Creating test file for 'products'
4. 8:06 - Adding test command and running it
5. 9:24 - Refactoring in order to implement supertest
6. 15:50 - mongodb-memory-server (Author recommends mocking the service instead, this is demonstrated in chapter 10. )
7. 18:47 - Adding more tests
8. 25:22 - JWT
9. 29:54 - .skip and .only flags for jest
10. 30:54 - Test file for 'Users' (The authors recommended way: mocking out service instead of mongodb-memory-server)
11. 36:26 - Mocking out and 'spying on' userService
12. 41:27 - Running the first User test
13. 43:57 - Clearing mocks in between tests
14. 46:05 - Reseting mocks in between tests
15. 47:16 - Test creation of user access token (Last test in the tutorial)
16. 52:34 - Bonus: Jest extension for VSCode
I'm really greatful for such tutorial. Before knowing about all taht stuuf, I asked myself : "How cna I test my API efficiently, with the most detailed tests possible and reproductibility ?". I almost started to cre&te tests using bash & curl, and starting my server as a daemon. I knew it was not the solution, so I didn't started yet. Thanks to your video, I know the correct way now.
THANK YOU SO MUCH for making this video. I've been looking for a compare and contrast of mongo-memory-server and mocking services for quite awhile!
This video is exacly what I need ATM.
ChaiHttp just decided to stop working, also it have problems with ES6 import modules, so I really needed to learn how to use Supertest, also I learned a lot of good practices in this video. Dude, this video is a complete GOLD.
Thank you very much, this is exactly what I need for my practice :> There are extremely few videos about Jest comparing to Cypess. Luckily I found yours :>
Best tutorial on TH-cam so far... and I've been searching for months
This is the best tutorial ever, thank you Tom!
I was absolutely clueless on how to mock my own functions. thank you so much
This channel is a gem! I appreciate that you cover important and practical topics (i.e testing, TS) in great detail. I've seen some videos on the 2 topics mentioned above but not as as it pertains to Nodejs. Thank you and you definitely deserve more views/subscribers.
Ooooo man. Don't miss even 10 seconds of this video (13:30 - 13:40). I missed the movement of `routes(app);` from ./app.ts to ./utils/server.ts and for 90 minutes was questioning my sanity as the API worked but the tests that exercised the route didn't. Because without this step, you get a 404 error by default for calling a route that doesn't exist - exactly the response code needed to pass the first product unit test - and the second unit test will never pass whatever you do.
This series is the best tutorial I've been following, thank you very much it is gold
Thank you so much! That's really kind of you.
Thank you so much man, learning alot from you.
Thanks for the awesom video. You made my TDD weekend fun.
I am so genuinely excited for this you sir are a beacon of light in the coding world lol starting it now👌🏽💯
That's really kind of you, even if it is a little odd :)
Hi Tom! Your authentication API video with typegoose and zod helped me tremendously. I know i will learn a lot from this, so Thank you for making this video!
I really like your use of multiple describe blocks here. I recently implemented something very similar for a project and have found having these tests is really helpful when refactoring. Thanks for the content!
Thank you :) I was a bit nervous about doing that in this tutorial because I didn't want to turn anyone off, but it's how I structure my tests and I find it really valuable.
Thanks for your efforts Tom. Love the video
This is awesome that your are expanding the same API. I hope this series keeps going!
Yeah, going to build a UI next to show how to use the access & refresh tokens.
Good video! I watched it for the second time.
Great testing video. Appreciate your time and effort with it. Found your channel a few weeks ago and have slowly been making my way through all the videos. :)
Thanks Chris! Glad you're enjoying the videos. Feel free to jump on the Discord server if you have any questions.
Thanks you so much this video helped me a lot in braking the code and testing .
Glad it helped!
Amazing tutorials as always, love your Typescript/Mongoose/Express series
now a curious to see
Hi Tom, thanks for the video. With the updated repo, if you follow along to the video you will find that you will not be able to get most of the mongodb-memory-server section to work.
1. You will not be able to get product.productId after calling createProduct because productId doesn't exist in either the ProductDocument or ProductInput interface. To fix this I had to put productId: String in the ProductDocument interface, not sure if that is the right approach but the tests passed after that
2. You will not be able to use signJwt because it expects 2 parameters now, not sure how to get around this one. I skimmed through the rest of the video and didn't see any fixes for that so I stopped trying here.
really helpful and fun, thank you.
Great video one of the best!
@41:10 line 6, const app = createApp(), createApp() is not defined.
Di you watch the part where I refactored the app?
Excellent ! Thank you so much
.set('Authorization', `Bearer ${jwt}`) not working any idea why?
Thanks!
is it possible for someone to replace where we used mongodb-memory-server with the real MongoDB server?
Thank you. Any video for complex json object test cases scripts example in jest?
For current versions of jsonwebtoken error `Error: secretOrPrivateKey must be an asymmetric key when using ES256` could be solved by adding `allowInsecureKeySizes: true,` option to signJwt function. Cheers.
Hello, thanks a lot for this tuto!
I have a problem with using the send method for a post/put request, I always get an error "Right-hand side of 'instanceof' is not callable"
What is the reason of this error?
which terminal you are using in VSCode?
Late comment but for some reason Jest times out when i'm running all of the test after I included the "should return a 200 and create the product"... but if I just run that test individually with '.only' then it doesnt time out and passes. I even tried extending the time but it still times out when running all of the tests.
When testing api with ject, do you actually need to have data in database?
Mock data is needed
When deploying the website should I also upload the test?
awesome tutorial🤌
can you please tell me which vs code extension you are using for highlighting the code block
Even if I mock the service for user it still calls mongodb and creates it in database. Any thoughts?
great job.... thx
Which extension are you using to get "intellisense"-isc behaviour in the terminal?
And also which extension to get the "Run | Debug" options over each "Describe"-block?
It's an autocomplete plugin for zsh
Thanks sir 😊
Hi, great video. I have a question tho, how do I mock a class service?
Just mock it like it's a function, because that's what it is
@@TomDoesTech Ok, thank you very much
Didn't watch the whole video, but was wondering if you do unit tests in the video?
great tutorial keep it up but why did you need ts-ignore on the mockReturnValue?
because the service function that we are mocking are async and they return a Promise, and in jest we are saying that it will return a Product.
So typescript is saying , that the return type is not the correct one.
helpful tutorial
Thanks you so much 👍🏼🎉🙏
is jest used in this video . what is the difference between jest and supertest ?
Yes, supertest is for sending HTTP requests in your tests
excellent 👏🏿👏🏿👏🏿
Nice video.
I got a problem with this, inside config folder, test.ts file have the private key, if I remove the private key from test.ts file config and create the custom-environment-variables.ts file to read the variable from an env file, it doesn't work because can't read the private key value, if I just paste the private key into the test.ts file all work again, any idea or config I am missing there?
If your problem is that it can't get the value when you're running the tests, it's because you haven't called dotenv.config() before running the tests. You should be able to add it to a script that runs before your tests run. Otherwise, just generate some public and private keys that you use for tests and put them in test.ts
@@TomDoesTech oh yeah, that was my problem, the dotenv.config() call, I used that on a file that call the one I was testing, but not on the actual file. Thanks
Awesome Tutorial +++++++++++++++ Thank you.
Hi Tom, first of all thank you for your dedication for wanting to help others. I wanted to ask you how would I do the same with testing the endpoint only, without controllers and services?
Why would you want to test the endpoint?
You could mock the controller and asset that it was called with the expected request and response objects.
@@TomDoesTech The endpoint has the logic inside, it's not split with controllers, so the logic is declared on the POST request (e.g signing up a user with jwt middleware) and returning a token. Its all inside the routes folder.
@@arberi99 Do it exactly the same as I did it in the video. Tests should care about implementation, all they should care about is that given X input, Y should happen or Y should be returned, how your function does that shouldn't have anything to do with the test.
Having said that, split your code up a bit and it will make your life easier. Check the video before this one to see how I did it: th-cam.com/video/BWUi6BS9T5Y/w-d-xo.html
Hi! Great tutorial! Just wanted to ask what file icon theme are you using on vscode? thank you!
I think it's just the default theme
@@TomDoesTech okay thank you!
You're awesome!
This is awesome!!! thank you for this lesson. One request, can we make it a full stack app i.e. can we build a frontend to consume this API? thank you once again.
I keep getting "TypeError: (0 , supertest_1.default) is not a function." What's the problem here?
Try importing supertest like `import * as supertest from 'supertest'`
@@TomDoesTech It worked thank you!
do we learn UNIT testing here?
Good content, but it's honestly really hard to watch, with everything jumping around on the screen. So please consider this, because it's really not easy on the eyes to consume your content, and it's hard to stay focused.
I haven't noticed that in your other videos though as much, but this one is kinda tough
I still hit that like button though, because there's still value in the video
do you have a video with javascript and not typescript?
Nope
Cool ❤❤❤❤❤❤
tldr version??
if anyone uses NPM run this command " *npm run test -- --watch* "
Please show us how to make the React front end to this API
Hey Mohamed, thank you for your kind comments. The video you're after is here: th-cam.com/video/oSz23pPBpFY/w-d-xo.html
Then you may want to add Google OAuth: th-cam.com/video/Qt3KJZ2kQk0/w-d-xo.html
you didnt explain clearly what is the difference between clearMocks and resetMocks? and what is the difference between supertest and plain axios?
Read the docs if the explanation wasn't clear enough. Supertest will start an instance of your application and run requests against it. Axios will just send a request to a given URL.
I didn't enjoy this video as much as the last one the explanations were too brief it felt like you were just rushing true it also I did not really understand that last test so well. i don't have much experience in testing so maybe that is the problem. looking forward to the next video in the series. Thank you Tom.
Thanks very much, great video. When testing 'user registration' for 'given username and password are valid', I kept getting a 'TypeError' => 'user.toJson is not a function' in the referencing user.controller.. I removed the toJson() from the omit function and test passed and the route still works without. Couldn't quite understand what the issue was? Testing removing toJson from the omit functions in user.service, the app broke on the spot.. tried JSON.stringify instead, he wasn't having it.. Anybody hat a similar problem? Tks, regards PS: just seeing now that in the controller the createUser() is called from where the user is returned through the omit function already... so in the controller I only return and send the user
I had the same issue, not sure if it is refactored somewhere in the 1st video, but repo shows "return res.send(user);" in the controller as well
Are these tests considered unit-tests or integration tests? I'm leaning towards unit-tests since there is not really a database interaction and you are only mocking a service. But what if in the service you make a call to the database and fetch some data, is it considered to be an integration test then?
Nice video btw! Wrote my first nodejs (ts) tests!
You could argue for both, they test across multiple units of code, but I'd they say are unit tests. Don't get too wrapped up in what a test is called, be more concerned with how it makes your software more reliable.
@@TomDoesTech thats what I’m thinking as well, but for my assignment I need to get the correct definition. If I describe it like that I think it will be sufficient enough. Thanks for the vid 👍
@@TomDoesTech While I do find this a confusing subject when it comes to end points and especially databases, it's to my understanding that if one were to mock the database or API in any fashion that means disconnecting the reliance on another component to be functioning properly in order for a test to pass, it's a unit test. The reverse being an integration test when the code tests multiple aspects of the program at the same time; i.e. we write a test that connects to express which in turn runs a function that connects to a database and creates or modifies or deletes a user for example.
The only confusion I have there is when we also consider end-to-end or end point testing which more or less involves connecting to say a page route, but does not involve connection to a database or other component? I find this especially confusing when using a template engine like .ejs where even a basic rout to get a page might also be required to render dynamic data. I suppose I could just write the tests to ignore the dynamic data aspect, focusing only on reaching status 200 or 302 for rerouting.
On a different note, I found in my current project that jest loved to assume the server was open even when it should be closing since I separated my server listen from the app just like you did here with server.js - importing the server separately.
let serverListen
// teardown
beforeEach(() => {
serverListen = require("../server.js");
});
afterEach(() => {
serverListen.close();
});
I found this little bit of code resolved the warning. I was running quite a few tests concurrently and solved those by changing serverListen = require("../server.js") to a specific port for each test file serverListen = app.listen("5006"). This was NOT always necessary, I just found it solved issues where one of my test suites would "butt heads" with another on and off. Great video overall. Good mocking content is actually tough to find in Javascript and since I am new, there's an amazon aws-sdk mocking issue driving me nuts atm hahaha. (I REALLY need to learn typescript and c#) >_
● Validation Error:
Preset ts-jest not found.
Configuration Documentation:
jestjs.io/docs/configuration
if ANYONE face this error, remove the present in jest.config.js file
anoying issue
Bro, no you have to instal ts-jest as a dependency: for example:
yarn add ts-jest -D
Hello) I am writing the fourth comment)
I don't see record about mongodb-memory-server in file 'package'. How I have to install it, globally or another way?
My comments are automatically being deleted....I have no idea why
That's weird. Sometimes YT does weird stuff with comments.
@@TomDoesTech apparently it just deleted my response to this
@@NathanielBabalola We should show TH-cam how to build a website that works!
@@TomDoesTech hahaha, we actually should.
why you dont use testing database? you are not testing much this way
Testing against a database is a nightmare to manage in CI pipelines. If you think it's not testing enough, then test against a database. I'm usually pretty happy with the test coverage doing it this way.
I keep getting a 403 on this test
describe("given the user is logged in ", () => {
it("should return a 200 and create product", async () => {
const jwt = signJwt(userPayload);
const { statusCode, body } = await supertest(app)
.post("/api/products")
.set("Authorization", `Bearer ${jwt}`)
.send(productPayload);
expect(statusCode).toBe(200);
expect(body).toEqual({});
});
});
everything works 100% on postman