TDD approach to write Unit/ Integartion using Jest/ Supertest for Express REST API.
- Setup app folder: npm init -y
- npm install express --save
- Create new file app.js and create express server
- Note: res.json sets content-type application/JSON response where res.send sets the content-type text/Html
- Verify the response on browser in "Response goes here"
- Create test folder: test: all test goes here. test\unit: create unit test under this folder. shopping-list.controller.test.js will contain unit tests.
- Create api controller folder: api\controller: hosts actual api controller. shopping-list.controller.js will contain all api code.
- setup jest: npm install jest --save-dev
- package.json: update scripts.test with following: "test": "jest"
- npm run test - run this command. This will fail as "Your test suite must contain at least one test."
- shopping-list.controller.test.js: require shopping-list.controller.
- Add "describe" and "it" section.
- npm run test: this will fail as ShoppingListController.addItem function is not implemented
- shopping-list.controller.js: implement addItem function signature
- npm run test: should pass
- package.json: update "test":"jest --watchAll" this will keep monitoring the changes on save, no need re-run test command
- Install MongoDB and Robo 3T. MongoDB Compass is also very useful.
- npm install mongoose --save
- Create schema /database/models/ShopItem.js
- ShopItem.js: define ShopItemSchema and export ShopItemModel
- shopping-list.controller.test.js: require ShopItemModel
- Use jest.fn() to mock mongoose create method
- create new test: it "should call ShopItemModel.create" - check console: to confirm the test failed
- shopping-list.controller.js: invoke ShopItemModel.create() inside the addItem() - observe test is passing
- Observe Mongoose "console.warn", follow the step mentioned in the url to enable node test environement. create jest.config.js in the root folder and add the mentioned content.
- re-run the test and confirm the warning is gone
- npm install node-mocks-http --save-dev
- shopping-list.controller.test.js: setup node-mocks-http and mock req, res
- Create ./test/mock-data/shop-item.json mock data file and use it in the test.
- Modify the test to pass req, res, next in ShoppingListController.addItem(req, res, next)
- Observe the test is failing, modify shopping-list.controller.js to accept req, res, next params
- Update .toBeCalled() to alledWith(newShopItem): monitor the test is failing.
- shopping-list.controller.js: pass req.body to ShopItemModel.create and test will pass now
- Create a new test to validate HTTP response code 201. it "should return HTTP response 201" test should fail at this time.
- shopping-list.controller.js: add following - res.status(201) to make the test pass
- To ensure response it send back modify the test and expect res._isEndCalled() toBeTruthy: test should fail at this moment.
- shopping-list.controller.js: res.status(201).send() append send() - all test should pass at this moment.
- shopping-list.controller.test.js: extract req, res and next to global beforeEach
- extract request.body to beforeEach of describe
- it "should return json body in response": use mockReturnValue to mock the response whenever the respective method is called.
- use _getJSONData() on res to get the data from the response. At this moment observe the test is failing.
- shopping-list.controller.js: modify the addItem method and return savedItem in json format by changing .send() to .json(savedItem)
- Observe the test is still failing.
- Test is still failing because the memory of expected object and the response object are different. Use toStrictEqual in test instead of toBe to validate the response