Skip to main content

Defining a server-driven interaction

Server-driven interactions are defined with willReceiveHttpRequest. During definition, ContractCase acts as the mock client: it sends the request from the interaction definition to your real running server, and checks that the response matches.

Because the real server runs during definition, there are two differences from defining an HTTP client contract:

  1. You don't write triggers or testResponse functions - ContractCase generates the client side itself.
  2. You implement state handlers at definition time, since the server needs to be put into the right state before each interaction runs.

Telling ContractCase where your server is

Start your server before the tests run, and tell ContractCase its base URL with the mockConfig configuration option:

defineContract(
{
consumerName: 'http request consumer',
providerName: 'http request provider',
stateHandlers,
mockConfig: {
http: {
// Replace this with your own server URL
baseUrlUnderTest: `http://localhost:${port}`,
},
},
},
(contract) => {
// ... interaction definitions go here
},
);

Defining an interaction

The request and response are described the same way as for HTTP client contracts - only the direction is reversed. runInteraction is used for interactions where the client code would succeed, and runRejectingInteraction (in Java, runThrowingInteraction) for interactions where a client would treat the response as an error:

describe('When the server is up', () => {
const state = inState('Server is up');

it('returns a healthy status', () =>
contract.runInteraction({
states: [state],
definition: willReceiveHttpRequest({
request: {
method: 'GET',
path: '/health',
headers: { accept: 'application/json' },
},
response: { status: 200, body: { status: 'up' } },
}),
}));
});

describe('When the server is down', () => {
it('returns an error status', () =>
contract.runRejectingInteraction({
states: [inState('Server is down')],
definition: willReceiveHttpRequest({
request: {
method: 'GET',
path: '/health',
},
response: { status: httpStatus(['4XX', '5XX']) },
}),
}));
});

All the usual Test Equivalence Matchers are available in both the request and the response.

State handlers during definition

Any states referenced by your interactions need state handlers - functions that put your running server into the named state. These work exactly the same way as state handlers during verification, and the same advice applies: mock the repository layer of your service if you can.

const stateHandlers: StateHandlers = {
'Server is up': () => {
mockHealthStatus = true;
},
'Server is down': () => {
mockHealthStatus = false;
},
'A user exists': {
setup: () => {
const userId = '42';
mockGetUser = (id) =>
id === userId ? { userId, name: 'John' } : undefined;
// Return the userId as a state variable
return { userId };
},
teardown: () => {
mockGetUser = () => undefined;
},
},
};

Next steps

Once your contract is defined, upload it to a broker so that your clients can verify it.