### Start Web Server in Same Process for Testing (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example shows how to start the backend web server within the same process as the tests. This approach is recommended to allow for advanced testing capabilities such as simulating events, customizing environment variables, and simplifying code coverage measurement. ```javascript const apiUnderTest = require('../api/start.js'); beforeAll(async () => { //Start the backend in the same process ``` -------------------------------- ### Install DB Schema with Production Technique in Node.js Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This code snippet demonstrates how to install the database schema using the same mechanism as production, typically a migration command. It ensures that all tests run with the necessary prerequisites, preventing bugs related to existing tables or columns. This is invoked in the global test setup. ```javascript // Create the DB schema. Done once regardless of the amount of tests module.exports = async () => { console.time('global-setup'); // ... await npmCommandAsPromise(['db:migrate']); // ... // 👍🏼 We're ready console.timeEnd('global-setup'); ``` -------------------------------- ### Node.js Script to Start Docker Compose Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript file, intended for use with Jest's global setup, utilizes the 'docker-compose' npm package to programmatically start all services defined in a docker-compose file. This automates the process of bringing up the necessary infrastructure before tests run, ensuring a consistent and isolated testing environment. ```javascript // global-setup.js const dockerCompose = require('docker-compose'); module.exports = async () => { await dockerCompose.upAll(); }; ``` -------------------------------- ### Jest Global Setup for Docker Compose Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This configuration sets up Jest to use a custom global setup file. The `globalSetup` option in `jest.config.js` points to a JavaScript file that will be executed once before all tests begin, ensuring the testing environment is ready. This approach centralizes environment setup and teardown. ```javascript // jest.config.js globalSetup: './example-application/test/global-setup.js'; ``` -------------------------------- ### Global Metadata Seeding in Node.js Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Demonstrates seeding metadata, such as country lists, globally once for all tests. This avoids redundant seeding per test file, optimizing setup time. It utilizes npm scripts for database seeding. ```javascript // Adding metadata globally. Done once regardless of the amount of tests module.exports = async () => { console.time('global-setup'); // ... await npmCommandAsPromise(['db:seed']); // Will create a countries (metadata) list. This is not related to the tests subject // ... // 👍🏼 We're ready console.timeEnd('global-setup'); }; ``` -------------------------------- ### Observability Check Example Test Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates how to test observability, ensuring correct error handling and proper logging/metrics are in place. It is particularly useful for validating how the application behaves during transaction failures and informs operations users. The specific file is 'createOrder.observabilityCheck.test.ts'. ```typescript import { checkObservability } from '../src/observability'; describe('checkObservability', () => { it('should log an error and emit a metric on failure', async () => { // Mock necessary dependencies (e.g., logger, metrics emitter) const mockLogger = { error: jest.fn() }; const mockMetrics = { increment: jest.fn() }; const error = new Error('Something went wrong'); await checkObservability(mockLogger, mockMetrics, error); expect(mockLogger.error).toHaveBeenCalledWith('Transaction failed:', error); expect(mockMetrics.increment).toHaveBeenCalledWith('transaction_failures'); }); it('should not log or emit metrics if there is no error', async () => { const mockLogger = { error: jest.fn() }; const mockMetrics = { increment: jest.fn() }; await checkObservability(mockLogger, mockMetrics, null); expect(mockLogger.error).not.toHaveBeenCalled(); expect(mockMetrics.increment).not.toHaveBeenCalled(); }); }); ``` -------------------------------- ### Control Server Start/Stop in Node.js Tests Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This snippet demonstrates how to expose start and stop functions for a web server, allowing tests to control its lifecycle. This prevents resource leaks and enables pre-initialization actions. It requires Node.js and potentially an Express framework. ```javascript const initializeWebServer = async () => { return new Promise((resolve, reject) => { // A typical Express setup expressApp = express(); defineRoutes(expressApp); connection = expressApp.listen(() => { resolve(expressApp); }); }); }; const stopWebServer = async () => { return new Promise((resolve, reject) => { connection.close(() => { resolve(); }); }); }; beforeAll(async () => { expressApp = await initializeWebServer(); }); afterAll(async () => { await stopWebServer(); }); ``` -------------------------------- ### API Testing with Axios HTTP Client (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Illustrates how to use the axios library as a pure HTTP client for API testing in Node.js. This example shows setting up an axios instance with a base URL and validating status codes, then using it to make POST and GET requests to test order creation and retrieval. It emphasizes avoiding testing frameworks that tightly couple tests to application code. ```javascript // basic-test.test.ts const axios = require('axios'); let axiosAPIClient; beforeAll(async () => { const apiConnection = await initializeWebServer(); const axiosConfig = { baseURL: `http://127.0.0.1:${apiConnection.port}`, validateStatus: () => true, }; // Create axios client for the whole test suite axiosAPIClient = axios.create(axiosConfig); // ... }); test('When asked for an existing order, Then should retrieve it and receive 200 response', async () => { const orderToAdd = { userId: 1, productId: 2, mode: 'approved', }; // Use axios to create an order const { data: { id: addedOrderId }, } = await axiosAPIClient.post(`/order`, orderToAdd); // Use axios to retrieve the same order by id const getResponse = await axiosAPIClient.get(`/order/${addedOrderId}`); // ... }); ``` -------------------------------- ### Simulate Network Chaos with Retries in JavaScript Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates simulating network chaos by introducing a temporary service unavailability (HTTP 503) followed by a successful response. This tests the resilience and retry mechanisms of the application when dealing with transient network issues. ```javascript test('When users service replies with 503 once and retry mechanism is applied, then an order is added successfully', async () => { //Arrange nock.removeInterceptor(userServiceNock.interceptors[0]); nock('http://localhost/user/') .get('/1') .reply(503, undefined, { 'Retry-After': 100 }); nock('http://localhost/user/').get('/1').reply(200); const orderToAdd = { userId: 1, productId: 2, mode: 'approved', }; //Act const response = await axiosAPIClient.post('/order', orderToAdd); //Assert expect(response.status).toBe(200); }); ``` -------------------------------- ### Run Database Migrations Before Tests (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This snippet demonstrates how to configure Jest to run database migrations using an npm script before all tests. It utilizes `jest.config.js` to specify a global setup file that executes migration commands. ```javascript // jest.config.js globalSetup: './example-application/test/global-setup.js'; // global-setup.js const npm = require('npm'); const util = require('util'); module.exports = async () => { // ... const npmCommandAsPromise = util.promisify(npm.commands.run); await npmCommandAsPromise(['db:migrate']); // Migrating the DB using a npm script before running any tests. // ... }; ``` -------------------------------- ### Assert New Data State Using Public API (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates how to assert the new data state by fetching it via the public API after creating it. It uses Axios to make POST and GET requests, and Jest's expect for assertions. This approach ensures that the API correctly returns the data, mirroring a user's interaction. ```javascript test('When adding a new valid order, Then should be able to retrieve it', async () => { //Arrange const orderToAdd = { userId: 1, productId: 2, mode: 'approved', }; //Act const { data: { id: addedOrderId }, } = await axiosAPIClient.post('/order', orderToAdd); //Assert by fetch the new order, and not only by the POST response const { data, status } = await axiosAPIClient.get(`/order/${addedOrderId}`); expect({ data, status, }).toMatchObject({ status: 200, data: { id: addedOrderId, userId: 1, productId: 2, }, }); }); ``` -------------------------------- ### Define Mocks Within Test Files (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Demonstrates how to define mocks either within the test setup or a beforeEach hook to ensure mocks are visible and their effects are understood. This approach avoids surprising behavior by keeping mock definitions close to their usage. ```javascript beforeEach(() => { // The email sender isn't directly related to the test's subject, so we put it in the // closest test hook instead of inside each test. sinon.restore(); sinon.stub(emailService, 'send').returns({ succeeded: true }); }); test('When ordered by a premium user, Then 10% discount is applied', async () => { // This response from an external service directly affects the test result, // so we define the mock inside the test itself. sinon.stub(usersService, 'getUser').returns({ id: 1, status: 'premium' }); //... }); ``` -------------------------------- ### Node.js Mocking: Cache-based vs. Module-based (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/mocking.md Demonstrates cache-based mocking (which fails in this example) and module-based mocking using Vitest for Node.js services. Cache-based mocking is simpler for CJS or object exports, while module-based mocking is necessary in other scenarios. It requires the 'vitest' library. ```javascript // service2.js export function service2Func() { console.log('the real service2'); } // service1.js import { service2Func } from './service2.js'; export function service1Func() { service2Func(); } // test-with-object-caching.js import * as s1 from './service1.js'; import * as s2 from './service2.js'; test('Check mocking', () => { sinon.stub(s2, 'service2Func').callsFake(() => { console.log('Service 2 mocked'); }); s1.service1Func(); }); // Prints "The real service2". Mocking failed ❌ ``` ```javascript // test-with-module-loading.js import { test, vi } from 'vitest'; import * as s1 from './service1.js'; import * as s2 from './service2.js'; test('Check mocking', () => { vi.spyOn(s2, 'service2Func').mockImplementation(() => { console.log('Service 2 mocked'); }); s1.service1Func(); }); // Prints "Service 2 mocked". Mocking worked ✅ ``` -------------------------------- ### Override Defaults with Unique Paths in JavaScript Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates how to override default test paths by creating unique ones, allowing for specific handling of corner cases such as non-existent users. It uses nock to intercept HTTP requests and axios for API calls. ```javascript 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); }); ``` -------------------------------- ### Fake Timers for Network Call Simulation in Node.js Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates how to use `sinon.useFakeTimers()` to simulate long network requests. It intercepts requests using `nock` and asserts that a 503 status is returned when a service doesn't reply within a specified time. This technique minimizes network call duration in tests. ```javascript test("When users service doesn't reply within 2 seconds, return 503", async () => { //Arrange const clock = sinon.useFakeTimers(); nock('http://localhost/user/') .get('/1', () => clock.tick(5000)) .reply(200); const orderToAdd = { userId: 1, productId: 2, mode: 'approved', }; //Act const response = await axiosAPIClient.post('/order', orderToAdd); //Assert expect(response.status).toBe(503); //Clean clock.uninstall(); }); ``` -------------------------------- ### Create Test Records Within Each Test (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates creating a specific order record within a test function using axios to interact with an API. This ensures the test is self-contained and doesn't rely on data created by other tests, improving isolation and reducing the risk of the domino effect. ```javascript test('When asked for an existing order, Then should retrieve it and receive 200 response', async () => { //Arrange - Create a record so we can later query for it and assert for is existence const orderToAdd = { userId: 1, productId: 2, mode: 'approved', }; await axiosAPIClient.post(`/order`, orderToAdd); //Next -> Invoke the route under test and asssert for something }); ``` -------------------------------- ### Test for Poisoned Messages in Docker Compose Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This example demonstrates how to configure a Docker Compose file for testing 'poisoned' messages. It sets up a PostgreSQL database service to simulate a dependency that might be involved in message processing. The goal is to ensure that when an invalid message is encountered, the consumer handles it gracefully, stays alive, and a monitoring metric is fired, rather than crashing the application. ```yml version: '3.6' services: db: image: postgres:11 command: postgres environment: - POSTGRES_USER=myuser - POSTGRES_PASSWORD=myuserpassword - POSTGRES_DB=shop ports: - '5432:5432' ``` -------------------------------- ### Promisify MQ Event in Node.js Test for Synchronization Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This Node.js test example demonstrates how to synchronize test execution with asynchronous message queue operations. It uses the `once` function to create a promise that resolves when the 'message-acknowledged' event is emitted by the `MQClient`. This pattern avoids polling and ensures assertions are made only after the message processing is complete. ```javascript // The test listen to the acknowledge/confirm message and knows when the operation is done test('Whenever a user deletion message arrive, then this user orders are also deleted', async () => { // Arrange // 👉🏼 HERE WE SHOULD add new orders to the system const getNextMQEvent = once(MQClient, 'message-acknowledged'); // Once function, part of Node, promisifies an event from EventEmitter // Act fakeMessageQueue.pushMessageToQueue('deleted-user', { id: addedOrderId }); // Assert const eventFromMessageQueue = await getNextMQEvent; // This promise will resolve once the message handling is done // Now we're certain that the operations is done and can start asserting for the results 👇 }); ``` -------------------------------- ### Type-Safe Mocks in TypeScript with Vitest/Jest Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/mocking.md Illustrates ensuring type safety when mocking functions in TypeScript using Vitest or Jest. It shows how `vi.mocked` helps catch type mismatches during mocking, preventing discrepancies between the mock's signature and the actual function's signature. This example assumes the use of 'vitest'. ```typescript // calculate-price.ts export function calculatePrice(): number { return 100; } // calculate-price.test.ts vi.mocked(calculatePrice).mockImplementation(() => { // Vitest example. Works the same with Jest return { price: 500 }; // ❌ Type '{ price: number; }' is not assignable to type 'number' }); ``` -------------------------------- ### Mocking External Services and User Data in Node.js Tests Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/mocking.md This snippet demonstrates how to use Sinon.js to mock external service calls within Node.js tests. It shows mocking an email service's 'send' method in a 'beforeEach' hook for general setup and mocking a user service's 'getUser' method directly within a specific test case when its response is crucial for the test outcome. ```javascript beforeEach(() => { // The email sender isn't directly related to the test's subject, so we put it in the // closest test hook instead of inside each test. sinon.restore(); sinon.stub(emailService, 'send').returns({ succeeded: true }); }); test('When ordered by a premium user, Then 10% discount is applied', async () => { // This response from an external service directly affects the test result, // so we define the mock inside the test itself. sinon.stub(usersService, 'getUser').returns({ id: 1, status: 'premium' }); //... }); ``` -------------------------------- ### Module vs. Cache-based Mocking in Node.js Testing Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Demonstrates the difference between cache-based mocking (using Sinon) and module-based mocking (using Vitest) in Node.js. Cache-based mocks modify imported objects directly, while module-based mocks intercept module imports. The example shows that module-based mocking is successful in achieving the desired outcome, whereas cache-based mocking fails in this specific scenario. ```javascript // service2.js export function service2Func() { console.log('the real service2'); } // service1.js import { service2Func } from './service2.js'; export function service1Func() { service2Func(); } // test-with-object-caching.js import * as s1 from './service1.js'; import * as s2 from './service2.js'; test('Check mocking', () => { sinon.stub(s2, 'service2Func').callsFake(() => { console.log('Service 2 mocked'); }); s1.service1Func(); }); // Prints "The real service2". Mocking failed ❌ // test-with-module-loading.js import { test, vi } from 'vitest'; import * as s1 from './service1.js'; import * as s2 from './service2.js'; test('Check mocking', () => { vi.spyOn(s2, 'service2Func').mockImplementation(() => { console.log('Service 2 mocked'); }); s1.service1Func(); }); // Prints "Service 2 mocked". Mocking worked ✅ ``` -------------------------------- ### Test Response Schema with Dynamic Fields (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This code example illustrates how to validate the structure and types of a response, particularly for fields with dynamic or unpredictable data like IDs or timestamps. It uses `expect.any(Number)` to assert that the `id` field is a number, ensuring the API contract is met without needing to know the exact value. ```javascript test('When adding a new valid order, Then should get back approval with 200 response', async () => { // ... //Assert expect(receivedAPIResponse).toMatchObject({ status: 200, data: { id: expect.any(Number), // Any number satisfies this test mode: 'approved', }, }); }); ``` -------------------------------- ### Test for Undesired Side Effects in Node.js API Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript code example shows how to test for unintended side effects in an API, specifically when deleting a record. It ensures that only the intended record is deleted by creating an additional record and verifying its existence after the deletion operation. This prevents accidental deletion of multiple records. ```javascript test('When deleting an existing order, Then should get a successful message', async () => { // Arrange const orderToDelete = { userId: 1, productId: 2, externalIdentifier: `id-${getShortUnique()}`, //unique value }; const { data: { id: orderToDeleteId } } = await axiosAPIClient.post('/order', orderToDelete); // Create another order to make sure the delete request deletes only the correct record const anotherOrder = { userId: 1, productId: 2, externalIdentifier: `id-${getShortUnique()}`, //unique value }; nock('http://localhost/user/').get(`/1`).reply(200, { id: 1, name: 'John', }); const { data: { id: anotherOrderId } } = await axiosAPIClient.post('/order', anotherOrder); // Act const deleteResponse = await axiosAPIClient.delete( `/order/${orderToDeleteId}`, ); const getOrderResponse = await axiosAPIClient.get(`/order/${anotherOrderId}`); // Assert expect(deleteResponse.status).toBe(204); // Assert anotherOrder still exists expect(getOrderResponse.status).toBe(200); }); ``` -------------------------------- ### Type-Safe Mocking with Vitest/Jest in TypeScript Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Illustrates how to ensure type safety when mocking functions in TypeScript using Vitest or Jest. The example shows a type error that occurs when a mocked implementation returns a value with a different type than expected. Using `vi.mocked` (or `jest.mocked`) helps catch such discrepancies at compile time, preventing runtime errors. ```typescript // calculate-price.ts export function calculatePrice(): number { return 100; } // calculate-price.test.ts vi.mocked(calculatePrice).mockImplementation(() => { // Vitest example. Works the same with Jest return { price: 500 }; // ❌ Type '{ price: number; }' is not assignable to type 'number' }); ``` -------------------------------- ### Git Commands for Cloning and Setting Up Repository Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/CONTRIBUTING.md These Bash commands are used to clone a GitHub repository, navigate into the directory, and set up the 'upstream' remote pointing to the original project. This is a foundational step for contributing to any open-source project. ```bash # Clone your fork of the repo into the current directory git clone https://github.com//integration-tests-a-z.git # Navigate to the newly cloned directory cd html5-boilerplate # Assign the original repo to a remote called "upstream" git remote add upstream https://github.com/testjavascript/integration-tests-a-z.git ``` -------------------------------- ### Docker Compose for Node.js Application Testing Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md A sample docker-compose.yml file to set up a PostgreSQL database service for testing Node.js applications. This configuration defines the PostgreSQL image, environment variables for credentials and database name, and exposes the default PostgreSQL port. ```yml # docker-compose file version: '3.6' services: db: image: postgres:11 command: postgres environment: - POSTGRES_USER=myuser - POSTGRES_PASSWORD=myuserpassword - POSTGRES_DB=shop ports: - '5432:5432' ``` -------------------------------- ### Git: Push Topic Branch to Fork Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/CONTRIBUTING.md This command pushes your completed topic branch to your forked repository on GitHub. This makes your changes available for others to review and for you to open a pull request. ```bash git push origin ``` -------------------------------- ### Context Data Seeding Per Suite in Node.js Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Illustrates seeding context data, like user records, once before all tests within a describe block (suite). This data is required for tests but not directly tested, improving efficiency by creating it only once per suite. Dependencies include a `createUser` function and `axiosAPIClient` for API interactions. ```javascript describe('/api', () => { let user; beforeAll(async () => { // Create context data once before all tests in the suite user = createUser(); }); describe('GET /order', () => { test('When asked for an existing order, Then should retrieve it and receive 200 response', async () => { //Arrange const orderToAdd = { userId: user.id, // Must provide a real user id but we don't care which user creates the order productId: 2, mode: 'approved', }; const { data: { id: addedOrderId }, } = await axiosAPIClient.post(`/order`, orderToAdd); ... }); }); }); ``` -------------------------------- ### Docker Compose for Database and Infrastructure Testing Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This Docker Compose configuration defines a PostgreSQL database service for testing purposes. It specifies the image, command-line arguments for performance, environment variables for connection, container name, port mapping, and uses a temporary file system for data to ensure a clean state for each test run. ```yaml # docker-compose.yml version: '3.6' services: database: image: postgres:11 command: postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off -c random_page_cost=1.0 environment: - POSTGRES_USER=myuser - POSTGRES_PASSWORD=myuserpassword - POSTGRES_DB=shop container_name: 'postgres-for-testing' ports: - '54310:5432' tmpfs: /var/lib/postgresql/data ``` -------------------------------- ### Test Mock Cleanup with Sinon Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/mocking.md Illustrates the essential practice of cleaning up mocks before and after tests using Sinon.js. The `beforeEach` hook restores all stubs and redefines common mocks, ensuring a clean state for each test. `afterAll` provides a final cleanup. ```javascript beforeEach(() => { sinon.restore(); // Redefine all common mocks to reset them in case a test modified them sinon.stub(emailService, 'send').returns({ succeeded: true }); }); afterAll(() => { sinon.restore(); }); ``` -------------------------------- ### Write Integration-Component Tests with Unit Testing Style (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Demonstrates writing integration-component tests in a style similar to unit tests. The tests are designed to be small, fast, and follow the Arrange-Act-Assert (AAA) pattern. This approach aims to provide the benefits of unit tests (developer experience, speed) while covering more ground by testing actual component interactions. ```javascript // basic-tests.test.ts test('When asked for an existing order, Then should retrieve it and receive 200 response', async () => { // Arrange const orderToAdd = { userId: 1, productId: 2, mode: 'approved', }; const { data: { id: addedOrderId }, } = await axiosAPIClient.post(`/order`, orderToAdd); // Act const getResponse = await axiosAPIClient.get(`/order/${addedOrderId}`); // Assert expect(getResponse).toMatchObject({ status: 200, data: { userId: 1, productId: 2, mode: 'approved', }, }); }); ``` -------------------------------- ### Git Commands for Creating and Managing Topic Branches Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/CONTRIBUTING.md These Bash commands demonstrate how to create a new Git branch for feature development or bug fixes and how to merge or rebase changes from the upstream development branch into your topic branch. This process helps in isolating changes and managing contributions effectively. ```bash git checkout -b git pull [--rebase] upstream master ``` -------------------------------- ### Partial Mocking with Sinon Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/mocking.md Demonstrates how to use Sinon.js to create a partial mock of an object, stubbing all its methods to return undefined. This approach helps in isolating specific functions for testing or simulating failures. ```javascript import sinon from 'sinon'; const myObject = { methodA: () => 'some value', methodB: () => 42, }; // Stub all functions to return undefined const stubbedObject = sinon.stub(myObject); console.log(stubbedObject.methodA()); // undefined ``` -------------------------------- ### Git Command for Pushing Changes to Fork Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/CONTRIBUTING.md This Bash command pushes your committed changes from your local topic branch to your fork on the remote repository (origin). This makes your changes available for creating a pull request. ```bash git push origin ``` -------------------------------- ### Git Commands for Updating Local Repository Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/CONTRIBUTING.md This sequence of Git commands in Bash is used to ensure your local master branch is up-to-date with the 'upstream' repository. It involves switching to the master branch and then pulling the latest changes. ```bash git checkout master git pull upstream master ``` -------------------------------- ### NVM Command for Node.js Version Management Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/CONTRIBUTING.md The 'nvm use' command is utilized to ensure that you are working with the same Node.js version as the project requires. NVM (Node Version Manager) allows developers to easily switch between different Node.js versions on their system. ```bash nvm use ``` -------------------------------- ### Optimize PostgreSQL for Testing (YAML) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This configuration snippet shows how to optimize a PostgreSQL database for testing within a Docker Compose environment. It specifies command-line arguments to the PostgreSQL server that trade durability for performance, such as disabling fsync and synchronous_commit. It also mounts the data directory as a tmpfs (RAM disk). ```yaml # docker-compose file version: "3.6" services: db: image: postgres:13 container_name: 'postgres-for-testing' // fsync=off means don't wait for disc acknowledgement command: postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off -c random_page_cost=1.0 tmpfs: /var/lib/postgresql/data # ... ``` -------------------------------- ### Organize Tests by API Routes (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript code snippet demonstrates how to structure tests using nested 'describe' blocks to represent API routes and their corresponding test suites. This hierarchical organization helps in mapping test failures to specific API endpoints. No external dependencies are required for this structure. ```javascript // basic-tests.test.js describe('/api', () => { describe('GET /order', () => { // ... }); describe('POST /orders', () => { // ... }); }); ``` -------------------------------- ### Test Backend Exit Doors Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This section outlines testing the five typical backend flow outcomes: Response, A new state, Calls to external services, Message queues, and Observability. The focus is on testing observable outcomes rather than internal implementation details. ```APIDOC ## Test the Five Known Backend Exit Doors ### Description When planning your tests, consider covering the five typical backend flow's outputs. Focus on testing observable outcomes that might affect the user, rather than the internal workings. ### Method N/A (Strategic Guideline) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ### The Five Exit Doors: 1. **Response:** Checking the correctness, schema, and HTTP status of the response after an action (e.g., API call). 2. **A new state:** Verifying that data modifications (e.g., in databases) occurred correctly after an action. 3. **Calls to external services:** Testing outgoing calls to external components (e.g., email, payment gateways) via HTTP or other transports. 4. **Message queues:** Ensuring messages are correctly placed in queues as an outcome of a flow. 5. **Observability:** Monitoring and testing error handling, logging, and metrics for operational insights. ``` -------------------------------- ### Node.js After-Each Database Cleanup Strategy Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript code snippet illustrates the 'after-each' database clean-up strategy for Node.js tests. It shows how to use Jest's `afterAll` or `afterEach` hooks to execute a cleanup function after all tests or after each individual test has run, respectively. This approach ensures a clean state for every test but can lead to issues in multi-process environments. ```javascript // After-each clean up afterAll(async () => { await new OrderRepository().cleanup(); }); // or afterEach(async () => { await new OrderRepository().cleanup(); }); ``` -------------------------------- ### JavaScript Fake Message Queue Client for Testing Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript code defines a simplistic fake Message Queue client class, `FakeMessageQueueProvider`, designed for testing purposes. It extends `EventEmitter` to allow tests to subscribe to events like message acknowledgment and sending. The fake simulates message consumption by storing a handler and directly invoking it when a message is pushed, enabling in-memory testing with high performance. ```javascript class FakeMessageQueueProvider extends EventEmitter { async ack() { this.emit('message-acknowledged', { event: 'message-acknowledged' }); //Let the test know that this happened } async sendToQueue(queueName, message) { this.emit('message-sent', message); } async consume(queueName, messageHandler) { // We just save the callback (handler) locally, whenever a message will put into this queue // we will fire this handler this.messageHandler = messageHandler; } async pushMessageToQueue(queue, newMessage) { this.messageHandler(newMessage); } } ``` -------------------------------- ### Node.js After-All Database Cleanup Strategy Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript code snippet demonstrates a global teardown function for Node.js integration tests, implementing an 'after-all' database clean-up strategy. It randomly triggers a cleanup operation, which is part of a recommended approach for managing test data integrity. This strategy is suitable for multi-process testing and provides a more realistic testing environment. ```javascript // After-all clean up (recommended) // global-teardown.js module.exports = async () => { // ... if (Math.ceil(Math.random() * 10) === 10) { await new OrderRepository().cleanup(); } }; ``` -------------------------------- ### Write Tests During Coding Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This section emphasizes the importance of writing tests concurrently with coding, rather than as an afterthought. It suggests finding a sweet spot between writing tests too early and too late to maximize their benefit as a safety net without hindering development. ```APIDOC ## Write Tests During Coding ### Description Write tests when it's most convenient—before or during coding—but never after everything is built. Waiting too long means losing the anti-regression safety net that tests provide. The goal is to write tests early enough to provide a safety net without slowing development. ### Method N/A (Strategic Guideline) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ### Considerations - Writing tests too early (e.g., strict TDD) can lead to unnecessary refactoring. - The ideal time is when requirements and implementation are clear enough. - Tests should be designed for frequent execution to catch regressions quickly. ``` -------------------------------- ### Define Default HTTP Responses Before Each Test (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Ensure a clean testing slate by defining default HTTP responses before each test using `beforeEach`. This prevents tests from interfering with each other. `nock.cleanAll()` is used in `afterEach` to clean up interceptors after each test, guaranteeing a fresh environment for every test execution. ```javascript // Create a one-time interceptor before every test beforeEach(() => { nock('http://localhost/user/').get(`/1`).reply(200, { id: 1, name: 'John', }); }); // Endure clean slate after each test afterEach(() => { nock.cleanAll(); }); ``` -------------------------------- ### Assert Entire HTTP Response Object in Tests Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Shows how to assert on the entire HTTP response object, including status, body, and headers, using `toMatchObject`. This provides a clear and concise way to verify the expected outcome of an API request. It is particularly useful when multiple fields need to be checked as a single unit. Dependencies include testing frameworks like Jest and HTTP clients like axios. ```typescript test('When asked for an existing order, Then should retrieve it and receive 200 response', async () => { // ... const getResponse = await axiosAPIClient.get(`/order/${addedOrderId}`); // Assert on entire HTTP response object expect(getResponse).toMatchObject({ status: 200, data: { userId: 1, productId: 2, mode: 'approved', }, }); }); ``` -------------------------------- ### Sign JWT Token for Authentication Testing Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Demonstrates how to sign a JSON Web Token (JWT) for authentication in tests. This approach authenticates by passing a valid token, mimicking client behavior and ensuring the same code path as production is exercised. It requires a secret to sign the token. ```javascript // Code example with signing JWT token ``` -------------------------------- ### Store Test Data in RAM Folder (YAML) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This YAML snippet configures a Docker Compose service to store its database data in a RAM folder using 'tmpfs'. This is particularly effective in Linux environments to reduce disk I/O and improve test performance by keeping the database files in memory. This optimization is most beneficial when other performance tuning methods are less impactful. ```yaml # docker-compose file version: "3.6" services: db: image: postgres:13 container_name: 'postgres-for-testing' // 👇 Stores the DB data in RAM folder. Works only in Linux tmpfs: /var/lib/postgresql/data # ... ``` -------------------------------- ### Teardown Infrastructure in CI Environments (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This snippet demonstrates how to conditionally shut down Docker Compose infrastructure only when running in a Continuous Integration (CI) environment. It uses the 'is-ci' package to detect the CI context and the 'docker-compose' package to manage the service lifecycle. This prevents unnecessary shutdowns on local developer machines. ```javascript // jest.config.js globalTeardown: './example-application/test/global-teardown.js' ``` ```javascript // global-teardown.js - clean-up after all tests const isCI = require('is-ci'); const dockerCompose = require('docker-compose'); module.exports = async () => { // Check if running CI environment if (isCI) { dockerCompose.down(); } }; ``` -------------------------------- ### Randomize Server Port in Node.js Tests Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This code illustrates how to specify a port in production while allowing the server to randomize a port during testing. Randomizing ports prevents collisions when running multiple test processes concurrently. It's applicable to Node.js applications, particularly those using frameworks like Express. ```javascript // api-under-test.js const initializeWebServer = async () => { return new Promise((resolve, reject) => { // A typical Express setup expressApp = express(); connection = expressApp.listen(webServerPort, () => { // No port resolve(expressApp); }); }); }; // test.js beforeAll(async () => { expressApp = await initializeWebServer(); // No port }); ``` -------------------------------- ### Isolate Component with HTTP Interceptor (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Intercept outgoing HTTP requests to simulate external API responses, ensuring component isolation. This is crucial for preventing noise, improving performance, and simulating various scenarios. Nock is a popular library for this purpose. It intercepts requests at the network level, keeping tests black-box. ```javascript // Intercept requests for 3rd party APIs and return a predefined response beforeEach(() => { nock('http://localhost/user/').get(`/1`).reply(200, { id: 1, name: 'John', }); }); ``` -------------------------------- ### Deny All Outgoing Requests by Default in JavaScript Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This code snippet shows how to deny all outgoing HTTP requests by default using nock, except for those explicitly allowed. This ensures component isolation and prevents unexpected external calls. It's typically set up in a `beforeAll` hook and disabled after tests. ```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'); }); ``` -------------------------------- ### Assert Outgoing Request Schema with Nock and Axios Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This snippet demonstrates how to assert the schema and content of outgoing HTTP requests made by a Node.js application using nock for mocking and axios for making requests. It ensures that sensitive data like recipient addresses are correctly formatted. This is crucial for preventing integration issues that might otherwise only be discovered in production. ```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}$/), }); }); ``` -------------------------------- ### Emit 'message-acknowledged' Event in Message Queue Client Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This JavaScript code defines the `consume` method for a message queue client. It processes incoming messages, calls a provided callback, acknowledges the message, and then emits a 'message-acknowledged' event. This event signals to listeners, like tests, that a message has been fully handled. ```javascript // message-queue-client.js. The MQ client/wrapper is throwing an event when the message handler is done async consume(queueName, onMessageCallback) { await this.channel.consume(queueName, async (theNewMessage) => { await onMessageCallback(theNewMessage); await this.ack(theNewMessage); // Handling is done, acknowledge the msg this.emit('message-acknowledged', eventDescription); // Let the tests know that all is over }); } ``` -------------------------------- ### Test Message Acknowledgment in Node.js Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md Tests the acknowledgment of messages after processing. It simulates putting a message in a queue, verifies the application's response, and asserts that the message was acknowledged by the queue. This ensures that processed messages are not re-queued, preventing duplicate processing. It also covers checking for non-acknowledgment in case of failures. ```javascript //Putting a delete-order message, checking the the app processed this correctly AND acknowledged test('Whenever a user deletion message arrive, then his orders are deleted', async () => { // Arrange // Add here a test record - A new order of a specific user using the API const fakeMessageQueue = await startFakeMessageQueue(); const getNextMQEvent = getNextMQConfirmation(fakeMessageQueue); // Act fakeMessageQueue.pushMessageToQueue('deleted-user', { id: addedOrderId }); // Assert const eventFromMessageQueue = await getNextMQEvent; // Check here that the user's orders were deleted expect(eventFromMessageQueue).toEqual([{ event: 'message-acknowledged' }]); }); ``` -------------------------------- ### Add Randomness to Unique Fields in Tests (JavaScript) Source: https://github.com/goldbergyoni/nodejs-testing-best-practices/blob/master/README.md This snippet demonstrates how to append a short, unique suffix to fields with unique constraints, such as `externalIdentifier`. This practice prevents test collisions when multiple tests attempt to insert the same value, especially if the database is not cleaned between tests. It enhances test isolation and reduces the need for manual data cleanup. ```javascript test('When adding a new valid order, Then should get back 200 response', async () => { //Arrange const orderToAdd = { userId: 1, productId: 2, mode: 'approved', externalIdentifier: `id-${getShortUnique()}`, //unique value }; //Act const receivedAPIResponse = await axiosAPIClient.post('/order', orderToAdd); // ... }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.