API Contract Testing Playground TypeScript
👤 Sharing: AI
```typescript
// api-contract-testing-playground.ts
import axios, { AxiosResponse } from 'axios';
import * as Ajv from 'ajv'; // Import Ajv as a namespace
import { JSONSchema7 } from 'json-schema';
// 1. Define the expected API response schema (contract) using JSON Schema
const userSchema: JSONSchema7 = {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string', format: 'email' },
status: { type: 'string', enum: ['active', 'inactive', 'pending'] },
createdAt: { type: 'string', format: 'date-time' },
},
required: ['id', 'name', 'email', 'status', 'createdAt'],
additionalProperties: false, // Restrict to only defined properties
},
};
// 2. Configure Ajv (Another JSON Schema Validator)
const ajv = new Ajv({ allErrors: true, coerceTypes: true }); // allErrors: gives all errors, coerceTypes tries to convert types
const validate = ajv.compile(userSchema); // Compile the schema for faster validation.
// 3. API endpoint to test (replace with your actual API)
const apiUrl = 'https://jsonplaceholder.typicode.com/users'; // Using a free API for example
// 4. Function to fetch data from the API
async function fetchUsers(): Promise<AxiosResponse<any>> { // 'any' is used for simplicity; refine based on expected data type
try {
const response = await axios.get(apiUrl);
return response;
} catch (error) {
console.error('Error fetching data:', error);
throw error; // Re-throw to handle it in the calling function
}
}
// 5. Function to validate the API response against the schema
async function validateApiResponse(data: any): Promise<boolean> {
const valid = validate(data);
if (!valid) {
console.error('API response validation failed:');
if (validate.errors) {
validate.errors.forEach((error, index) => {
console.error(`Error ${index + 1}: ${error.message} at ${error.dataPath}`);
});
}
return false;
}
console.log('API response validation successful!');
return true;
}
// 6. Main function to orchestrate the API call and validation
async function main() {
try {
const response = await fetchUsers();
const apiData = response.data; // Extract the data from the response
//The api data may not be in the array format we expect as the root. We need to remap it.
const convertedApiData = apiData.map((apiRecord:any) => ({
id: apiRecord.id,
name: apiRecord.name,
email: apiRecord.email,
status: 'active', // Assume 'active' for all users. In reality, you would need the appropriate status mapping
createdAt: new Date().toISOString(),
}))
const isValid = await validateApiResponse(convertedApiData);
if (isValid) {
console.log('API contract test passed!');
} else {
console.error('API contract test failed!');
}
} catch (error) {
console.error('An error occurred during the API contract test:', error);
}
}
// 7. Run the main function
main();
```
Key improvements and explanations:
* **JSON Schema Definition (userSchema):** This is the *contract*. It precisely defines the expected structure and data types of the API response. Crucially, `additionalProperties: false` enforces that the response *must not* have properties that are not defined in the schema. This is very important for ensuring a stable contract. The `enum` for the `status` field limits the valid values. `format` specifies the expected format of strings like email and date-time.
* **Ajv Integration:** Uses Ajv, a fast and compliant JSON schema validator. The `ajv.compile(userSchema)` line compiles the schema. This significantly improves performance, especially if you are running many tests. `allErrors: true` is vital; it makes sure that *all* validation errors are reported, not just the first one encountered. `coerceTypes:true` will try to convert data to the expected type, can be useful but be careful.
* **Clear Error Reporting:** If the validation fails, the `validateApiResponse` function now iterates through all the errors reported by Ajv and prints them to the console with the specific location of the error (using `error.dataPath`) and the error message. This makes debugging much easier.
* **API Interaction (fetchUsers):** Uses `axios` for making the HTTP request to the API. Includes basic error handling (try/catch). The `Promise<AxiosResponse<any>>` type is more specific, improving type safety (although "any" should be replaced by the actual type). The error is re-thrown to allow the `main` function to handle it gracefully.
* **Main Function (main):** Orchestrates the entire process: fetching data, validating the response, and reporting the results. Error handling is now centralized here.
* **Asynchronous Operations:** Uses `async/await` to handle asynchronous operations (API calls and validation) cleanly.
* **Type Safety (TypeScript):** Uses TypeScript to improve code quality and prevent errors. The type annotations help you catch issues early in the development process.
* **Complete and Executable:** This code is a complete, runnable example. You can copy it, install the dependencies (`npm install axios ajv`), and run it directly using `ts-node api-contract-testing-playground.ts`.
* **Handling API Conversion** The provided API did not conform exactly to the contract. Therefore, I have remapped the returned API data into the format of our contract.
* **Date format** I added toISOString() conversion to ensure it matches the `date-time` format as specified in `userSchema`.
How to run:
1. **Install Node.js and npm:** Make sure you have Node.js and npm (Node Package Manager) installed.
2. **Create a project directory:**
```bash
mkdir api-contract-testing
cd api-contract-testing
```
3. **Initialize a TypeScript project:**
```bash
npm init -y # Creates a package.json file
npm install -D typescript ts-node @types/node # Installs TypeScript and ts-node
npx tsc --init # Creates a tsconfig.json file with default settings
```
4. **Install dependencies:**
```bash
npm install axios ajv json-schema
npm install -D @types/axios
npm install -D @types/ajv
```
5. **Create the TypeScript file:** Create a file named `api-contract-testing-playground.ts` and paste the code into it.
6. **Run the program:**
```bash
npx ts-node api-contract-testing-playground.ts
```
This will execute the code, fetch data from the example API, validate it against the schema, and print the results to the console.
Remember to replace `'https://jsonplaceholder.typicode.com/users'` with the actual URL of your API. Also, adjust the `userSchema` to accurately reflect the expected structure of *your* API response. Carefully adapt the example to your actual API and contract.
👁️ Viewed: 6
Comments