Instruction to Deploy Contract Test between NextJS and NestJS with Pact
Introduction
- Contract Testing is an integration testing method focused on verifying the interaction between a Consumer (the service user) and a Provider (the service provider). Instead of testing the entire system, Contract Testing ensures that both parties adhere to a shared covenant (contract). Key benefits include: early detection of non-compatible errors between Frontend and Backend, reduction of dependency on complex staging environments and enabling API changes to be made more confidently without breaking the partner's application.
- Pact is currently the most popular Consumer-Driven Contract Testing tool. It allows the Consumer to define expectations regarding the response from the Provider and then packages these expectations into a JSON file (Pact file). Pact's advantages lie in supporting multiple languages, automatically generating documentation based on test cases and having robust integration capabilities into CI/CD pipelines to ensure the Provider always meets the Consumer's exact requirements before deployment.
Prerequisites
This article continues to build upon previous articles, so detailed source code will not be provided. Please refer back if you need the full code.
Detail
Please install the following package into both NextJS and NestJS projects:
When applying Contract Testing, we have two approaches:
- Consumer-Driven Contract Testing (CDCT): meaning the Frontend (API user) initiates requirements first.
- Why does Pact prioritize Frontend definition first?
- Pact's philosophy is: Backend should only provide what the Frontend truly needs.
- If the Backend defines it, it often returns excess data (e.g., returning both password_hash, internal_id which the Frontend does not use).
- When Frontend defines it first, Backend will know exactly which fields they need to avoid "accidentally" deleting those critical fields.
- Provider-Driven / Bi-Directional Contract Testing: if the Backend team wants to proactively define the schema first, we still have a workaround.
- The Backend can proactively define the API schema on Swagger and the Frontend team will use that data to write contract tests, thus returning the workflow back to the beginning like CDCT.
Summarized Workflow will be as follows:
- The FE team as the consumer defines the API including necessary fields to be used.
- Run the contract test to generate the JSON file.
- Share this JSON file with the BE team (can be via a sub repo to avoid manual sharing).
- The BE team as the provider when implementing a feature only needs to ensure it passes the contract test from this JSON file.
- Later, before deployment, simply run the contract test again to know if changes in the code base affect the APIs in use.
Note that when running contract test on the FE side, it will not involve calling the server directly (as the BE might not even have implemented that feature yet), only the contract test run from the BE side involves actual API calling.
NextJS Project
Create test/ChatBot/contract.test.ts as follows:
The code above defines a Consumer Test on the NextJS side. It uses PactV3 to initialize a contract between "NextJS-Chat-Frontend" and "NestJS-AI-Backend". In it, the given method sets the Provider state, uponReceiving describes the request purpose and withRequest defines the endpoint to call. The willRespondWith part uses Matchers to regulate the returned data type (here a list of objects where id, name, lastMsg are strings). Finally, executeTest will run a simulated server to perform actual API calling and verify if the received data matches the commitment.
When the test runs successfully, it will automatically generate the pacts/NextJS-Chat-Frontend-NestJS-AI-Backend.json file with similar content:
This is the Pact (contract) file automatically generated in JSON format. It contains all the detailed information about the interactions defined in the previous step, including information about the Consumer, Provider, required request (request) and expected response (response). Specifically, the matchingRules part records data type checking rules (match by type) instead of exact values, making verification on the Provider side more flexible while ensuring the required data structure.
NestJS Project
Create test/pact-provider.spec.ts
This code performs the verification task (Verification) on the Provider (NestJS) side. First, it initializes the actual NestJS application and runs on port 3001. Then, the Verifier object is used to read the Pact file created from the Frontend. It will automatically send the simulated requests within the contract file to the running NestJS server and compare the returned results with what the Consumer expects. The stateHandlers part is usually used to initialize initial data (such as inserting data into the database) to prepare for testing; here, when nothing is defined, it only checks if the actual API request and response match the content from the pact file.
Test result when running:
You can modify the content of this controller/conversations.controller.ts file, for example, I delete the id field as follows:
Result when testing again:
You can see that it has reported a missing key id error accordingly. This way, before deploying in the future, you only need to run the test again to know if your changes affect the frontend team's integration.
Happy coding!
Comments
Post a Comment