Added nock

This commit is contained in:
Yoni Goldberg
2023-05-08 11:46:01 +03:00
parent f4fb78efbc
commit d5aff223ef
3 changed files with 91 additions and 55 deletions

View File

@ -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

View File

@ -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
![alt text](../../assets/images/circleci.png "API error handling")
### Jenkins - sophisticated and robust CI
![alt text](../../assets/images/jenkins_dashboard.png "API error handling")
<br/><br/>

View 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}$/),
});
});
```