mirror of
https://github.com/goldbergyoni/nodebestpractices.git
synced 2026-03-13 08:40:16 +08:00
Added nock
This commit is contained in:
16
README.md
16
README.md
@@ -782,6 +782,22 @@ All statements above will return false if used with `===`
|
||||
|
||||
🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md)
|
||||
|
||||
## ![✔] 4.14 Specify a port in production, randomize in testing
|
||||
|
||||
**TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port
|
||||
|
||||
**Otherwise:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default
|
||||
|
||||
🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md)
|
||||
|
||||
## ![✔] 4.15 Test the five possible outcomes
|
||||
|
||||
**TL;DR:** When testing a flow, ensure to cover the five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow, consider writing a test for each: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices)
|
||||
|
||||
**Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue? there are typically more outcomes to consider than what meets the eye
|
||||
|
||||
🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md)
|
||||
|
||||
<br/><br/><br/>
|
||||
|
||||
<p align="right"><a href="#table-of-contents">⬆ Return to top</a></p>
|
||||
|
||||
BIN
assets/images/The backend testing checklist.png
Normal file
BIN
assets/images/The backend testing checklist.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
31
sections/testingandquality/randomize-port.md
Normal file
31
sections/testingandquality/randomize-port.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Specify a port in production, randomize in testing
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### One Paragraph Explainer
|
||||
|
||||
When writing component/integration tests, the web server should be started by the tests in the same process - this opens the door for many desirable testing features like mocking, coverage, and more. In a multi-process test runner, multiple web server instances will be opened. If these instances try to open the same port, they will collide. In testing only, let the server randomize a port to prevent collisions. This can easily achieved by providing an [ephemeral port](https://en.wikipedia.org/wiki/Ephemeral_port), the number zero, so the operating system will allocate an available port
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Code Example – starting the web server with testing in-mind
|
||||
|
||||
```javascript
|
||||
// api-under-test.js
|
||||
const initializeWebServer = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Fixed port in production, a zero port (ephemeral) for testing
|
||||
const webServerPort = process.env.PORT ? process.env.PORT : 0;
|
||||
expressApp = express();
|
||||
connection = expressApp.listen(webServerPort, () => {
|
||||
// No port
|
||||
resolve(expressApp);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// test.js
|
||||
beforeAll(async () => {
|
||||
expressApp = await initializeWebServer(); // No port
|
||||
});
|
||||
```
|
||||
23
sections/testingandquality/test-five-outcomes.md
Normal file
23
sections/testingandquality/test-five-outcomes.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Test the five potential outcomes
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### One Paragraph Explainer
|
||||
|
||||
When planning your tests, consider covering the five typical flow's outputs. When your test is triggering some action (e.g., API call), a reaction is happening, something meaningful occurs and calls for testing. Note that we don't care about how things work. Our focus is on outcomes, things that are noticeable from the outside and might affect the user. These outcomes/reactions can be put in 5 categories:
|
||||
|
||||
• Response - The test invokes an action (e.g., via API) and gets a response. It's now concerned with checking the response data correctness, schema, and HTTP status
|
||||
|
||||
• A new state - After invoking an action, some data is probably modified. For example, when updating a user - It might be that the new data was not saved. Commonly and mistakenly, testers check only the response and not whether the data is updated correctly. Testing data and databases raises multiple interesting challenges that are greatly covered below in the 📗 section 'Dealing with data'
|
||||
|
||||
• External calls - After invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card. Anything that goes outside and might affect the user - Should be tested. Testing integrations is a broad topic which is discussed in the 📗 section 'Testing integrations' below
|
||||
|
||||
• Message queues - The outcome of a flow might be a message in a queue. In our example application, once a new order was saved the app puts a message in some MQ product. Now other components can consume this message and continue the flow. This is very similar to testing integrations only working with message queues is different technically and tricky. The 📗 section 'Message Queues' below delve into this topic
|
||||
|
||||
• Observability - Some things must be monitored, like errors or remarkable business events. When a transaction fails, not only we expect the right response but also correct error handling and proper logging/metrics. This information goes directly to a very important user - The ops user (i.e., production SRE/admin). Testing error handler is not very straighforward - Many types of errors might get thrown, some errors should lead to process crash, and there are many other corners to cover. We plan to write the 📗 section on 'Observability and errors' soon
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Example: The backend testing checklist
|
||||
|
||||

|
||||
Reference in New Issue
Block a user