mirror of
https://github.com/goldbergyoni/nodebestpractices.git
synced 2025-10-27 19:17:13 +08:00
Added nock
This commit is contained in:
@ -766,13 +766,13 @@ All statements above will return false if used with `===`
|
||||
|
||||
<br/><br/>
|
||||
|
||||
## ![✔] 4.12 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world)
|
||||
## ![✔] 4.12 Mock responses of external HTTP services
|
||||
|
||||
**TL;DR:** Your continuous integration platform (CICD) will host all the quality tools (e.g. test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of a complex setup that demands a steep learning curve. Nowadays, it has become much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully
|
||||
**TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production
|
||||
|
||||
**Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup
|
||||
**Otherwise:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow
|
||||
|
||||
🔗 [**Read More: Choosing CI platform**](./sections/testingandquality/citools.md)
|
||||
🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md)
|
||||
|
||||
## ![✔] 4.13 Test your middlewares in isolation
|
||||
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
# Carefully choose your CI platform
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### One Paragraph Explainer
|
||||
|
||||
The CI world used to be the flexibility of [Jenkins](https://jenkins.io/) vs the simplicity of SaaS vendors. The game is now changing as SaaS providers like [CircleCI](https://circleci.com/) and [Travis](https://travis-ci.org/) offer robust solutions including Docker containers with minimum setup time while Jenkins tries to compete on 'simplicity' segment as well. Though one can setup rich CI solution in the cloud, should it required to control the finest details Jenkins is still the platform of choice. The choice eventually boils down to which extent the CI process should be customized: free and setup free cloud vendors allow to run custom shell commands, custom docker images, adjust the workflow, run matrix builds and other rich features. However, if controlling the infrastructure or programming the CI logic using a formal programming language like Java is desired - Jenkins might still be the choice. Otherwise, consider opting for the simple and setup free cloud option
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Code Example – a typical cloud CI configuration. Single .yml file and that's it
|
||||
|
||||
```yaml
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:4.8.2
|
||||
- image: mongo:3.4.4
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install npm wee
|
||||
command: npm install
|
||||
test:
|
||||
docker:
|
||||
- image: circleci/node:4.8.2
|
||||
- image: mongo:3.4.4
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Test
|
||||
command: npm test
|
||||
- run:
|
||||
name: Generate code coverage
|
||||
command: './node_modules/.bin/nyc report --reporter=text-lcov'
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
prefix: coverage
|
||||
|
||||
```
|
||||
|
||||
### Circle CI - almost zero setup cloud CI
|
||||
|
||||

|
||||
|
||||
### Jenkins - sophisticated and robust CI
|
||||
|
||||

|
||||
|
||||
<br/><br/>
|
||||
87
sections/testingandquality/mock-external-services.md
Normal file
87
sections/testingandquality/mock-external-services.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Mock responses of external HTTP services
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### One Paragraph Explainer
|
||||
|
||||
Isolate the component under test by intercepting any outgoing HTTP request and providing the desired response so the collaborator HTTP API won't get hit. Nock is a great tool for this mission as it provides a convenient syntax for defining external services behavior. Isolation is a must to prevent noise and slow performance but mostly to simulate various scenarios and responses - A good flight simulator is not about painting clear blue sky rather bringing safe storms and chaos. This is reinforced in a Microservice architecture where the focus should always be on a single component without involving the rest of the world. Though it's possible to simulate external service behavior using test doubles (mocking), it's preferable not to touch the deployed code and act on the network level to keep the tests pure black-box. The downside of isolation is not detecting when the collaborator component changes and not realizing misunderstandings between the two services - Make sure to compensate for this using a few contract or E2E tests
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Code Example – a simple mock using nock
|
||||
|
||||
```javascript
|
||||
// Intercept requests for internal or 3rd party APIs and return a predefined response
|
||||
beforeEach(() => {
|
||||
nock("http://localhost/user/").get(`/1`).reply(200, {
|
||||
id: 1,
|
||||
name: "John",
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Code Example – simulating an important scenario inside the test
|
||||
|
||||
```javascript
|
||||
// Using an uncommon user id (7) and create a compatible interceptor
|
||||
test("When the user does not exist, return http 404", async () => {
|
||||
//Arrange
|
||||
const orderToAdd = {
|
||||
userId: 7,
|
||||
productId: 2,
|
||||
mode: "draft",
|
||||
};
|
||||
|
||||
nock("http://localhost/user/").get(`/7`).reply(404, {
|
||||
message: "User does not exist",
|
||||
code: "nonExisting",
|
||||
});
|
||||
|
||||
//Act
|
||||
const orderAddResult = await axiosAPIClient.post("/order", orderToAdd);
|
||||
|
||||
//Assert
|
||||
expect(orderAddResult.status).toBe(404);
|
||||
});
|
||||
```
|
||||
|
||||
### Code Example – preventing requests from going outside to the real-world
|
||||
|
||||
```javascript
|
||||
beforeAll(async () => {
|
||||
// ...
|
||||
// ️️️Ensure that this component is isolated by preventing unknown calls
|
||||
nock.disableNetConnect();
|
||||
// Enable only requests for the API under test
|
||||
nock.enableNetConnect("127.0.0.1");
|
||||
});
|
||||
```
|
||||
|
||||
### Code Example – ensuring that the outgoing request schema is correct
|
||||
|
||||
```javascript
|
||||
// ️️️Assert that the app called the mailer service appropriately with the right input
|
||||
test("When order failed, send mail to admin", async () => {
|
||||
//Arrange
|
||||
// ...
|
||||
let emailPayload;
|
||||
nock("http://mailer.com")
|
||||
.post("/send", (payload) => ((emailPayload = payload), true))
|
||||
.reply(202);
|
||||
const orderToAdd = {
|
||||
userId: 1,
|
||||
productId: 2,
|
||||
mode: "approved",
|
||||
};
|
||||
|
||||
//Act
|
||||
await axiosAPIClient.post("/order", orderToAdd);
|
||||
|
||||
// ️️️Assert
|
||||
expect(emailPayload).toMatchObject({
|
||||
subject: expect.any(String),
|
||||
body: expect.any(String),
|
||||
recipientAddress: expect.stringMatching(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/),
|
||||
});
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user