Contract Testing with Jest for a Simple API
This challenge focuses on implementing contract testing using Jest for a hypothetical, simple API. Contract testing is crucial for ensuring that different parts of a system (like a frontend and backend) communicate correctly, even if they are developed independently. You will simulate an API and then write tests to verify its contract.
Problem Description
You are tasked with creating a contract test suite using Jest for a simplified API that manages a list of User objects. The API has two main endpoints:
GET /users: Retrieves a list of all users.POST /users: Adds a new user to the list.
You will first define the "contract" of this API by specifying the expected structure of the User object and the request/response formats for the endpoints. Then, you will write Jest tests that verify whether a given implementation of this API adheres to this contract.
Key Requirements:
- Define the User contract: The
Userobject should have at leastid(string, unique),name(string), andemail(string, valid email format). - Define API endpoint contracts:
GET /usersshould return an array ofUserobjects.POST /usersshould accept aUserobject (without anid, as it's generated server-side) and return the newly createdUserobject with itsid.
- Implement Contract Tests: Write Jest tests that assert these expectations. You'll need to simulate the API calls.
- Test for expected success responses: Ensure that successful requests return data in the correct format.
- Test for basic error handling (optional but recommended): Consider how invalid input to
POST /usersmight be handled (e.g., missing required fields).
Expected Behavior:
GET /users: When called, it should return a JSON array representing the current users.POST /users: When called with a valid user payload, it should return the created user object, including a server-generatedid.
Edge Cases to Consider:
- An empty user list for
GET /users. - Attempting to
POST /userswith missing required fields (e.g., nonameoremail).
Examples
Example 1: GET /users - Successful Response
Let's assume a mock API that currently has one user.
// Mock API State
let users = [
{ id: "abc-123", name: "Alice Smith", email: "alice@example.com" }
];
// Mock API Call for GET /users
// fetch('/users') would return this
// Expected Output
[
{
"id": "abc-123",
"name": "Alice Smith",
"email": "alice@example.com"
}
]
Explanation: The GET /users endpoint returns an array containing a single user object, matching the defined User contract.
Example 2: POST /users - Successful Creation
Let's assume an empty user list initially.
// Mock API State
let users = [];
// Mock API Call for POST /users with a new user payload
// fetch('/users', { method: 'POST', body: JSON.stringify({ name: "Bob Johnson", email: "bob@example.com" }) }) would return this
// Expected Output
{
"id": "generated-id-456", // The ID should be a string and uniquely generated
"name": "Bob Johnson",
"email": "bob@example.com"
}
Explanation: The POST /users endpoint accepts a user payload without an id, creates a new user with a server-generated id, and returns the complete user object.
Example 3: POST /users - Invalid Payload
// Mock API State
let users = [];
// Mock API Call for POST /users with an invalid payload
// fetch('/users', { method: 'POST', body: JSON.stringify({ name: "Charlie" }) }) would ideally return an error
// Expected Output (or similar error response)
{
"error": "Invalid payload. 'email' is required."
}
Explanation: The API should reject requests with missing required fields for user creation and return an informative error.
Constraints
- You will be working with TypeScript.
- Your contract tests should be written using Jest.
- You need to simulate the API responses. You do not need to set up a real server. You can mock
fetchor create simple functions that mimic API calls. - The
idfield for new users must be a string. - The
emailfield must adhere to a basic email format validation (e.g., contain '@' and '.').
Notes
- Consider using Jest's mocking capabilities to control API responses.
- Think about how you'll represent the "contract" itself in your code. This could be through TypeScript interfaces and type guards.
- For this challenge, you don't need to implement the actual API logic; your focus is solely on writing tests that verify the contract.
- Start by defining your
Userinterface and then write tests for each endpoint's contract. - When simulating
POST /users, you'll need a mechanism to generate unique IDs. A simple counter or UUID generator can be used for this simulation.