Skip to main content

Plugins

Plugins are the fundamental extension mechanism in Pact. Using the Plugin Framework, you can combine different plugins with the various in-built interaction types to model most interactions in your architecture.

See https://docs.pact.io/plugins for more on this topic.

Supported Plugins

Publicly available plugins are listed here: https://docs.pact.io/plugins/directory.

Alternatively, you can get an up-to-date list of available plugins via the CLI.

Plugins may be created for internal use cases and not published to the internet, and may also be used in your tests.

Matching / Expressions

Like regular Pact tests, plugins can support expressions (not all plugins do, so please be sure to check the docs for the plugin) enabling flexible matching.

HTTP Interactions

Consumer

When using a plugin, each plugin is responsible for defining its inputs/outputs. It is usually a JSON structure that represents the interaction. You will need to consult each plugin to understand what data structure should be supplied to your plugins and on which part of the interaction to supply them (e.g. the request or the response).

For example, this uses the Matt Plugin, a fictional protocol.

const mattRequest = `{"request": {"body": "hello"}}`;
const mattResponse = `{"response":{"body":"world"}}`;

await pact
.addInteraction()
.given('the Matt protocol exists')
.uponReceiving('an HTTP request to /matt')
.usingPlugin({
plugin: 'matt',
version: '0.1.1',
})
.withRequest('POST', '/matt', (builder) => {
builder.pluginContents('application/matt', mattRequest);
})
.willRespondWith(200, (builder) => {
builder.pluginContents('application/matt', mattResponse);
})
.executeTest((mockserver) => {
return axios
.request({
baseURL: mockserver.url,
headers: {
'content-type': 'application/matt',
Accept: 'application/matt',
},
data: generateMattMessage('hello'),
method: 'POST',
url: '/matt',
})
.then((res) => {
expect(parseMattMessage(res.data)).to.eq('world');
});
});

This looks just like other tests, however note the early inclusion of usingPlugin. This changes the functions available to the HTTP test, allowing plugins to be used on the request or response portion of the request.

In this case, we want to use a new content type: application/matt.

Provider

The verification side of this test is no different to a usual verification, as it's just an HTTP test. Any required plugins will automatically be loaded by the framework if not present, or an error will be shown should the plugin installation fail.

Asynchronous Messages

This feature has yet to be added

Synchronous Messages

Consumer

As per the HTTP interaction, calling usingPlugin early in the builder allows two additional methods: withPluginContents to specify the plugin contents, and startTransport to optionally start a transport for this test, if the plugin has one (for example, a gRPC service or in this case, a TCP server).

const pact = new PactV4({
consumer: 'myconsumer',
provider: 'myprovider',
spec: SpecificationVersion.SPECIFICATION_VERSION_V4,
logLevel: (process.env.LOG_LEVEL as LogLevel) || 'error',
});

describe('with MATT protocol', async () => {
it('generates a pact with success', () => {
const mattMessage = `{"request": {"body": "hellotcp"}, "response":{"body":"tcpworld"}}`;

return pact
.addSynchronousInteraction('a MATT message')
.usingPlugin({
plugin: 'matt',
version: '0.1.1',
})
.withPluginContents(mattMessage, 'application/matt')
.startTransport('matt', HOST)
.executeTest(async (tc) => {
const message = await sendMattMessageTCP(
'hellotcp',
HOST,
tc.port
);
expect(message).to.eq('tcpworld');
});
});

Provider

The verification side of this interaction needs you to specify additional transports on the verifier. In this case, where to send the TCP traffic in additional to the HTTP traffic. The rest of the verifier is the same as a normal verification:

        const v = new Verifier({
providerBaseUrl: `http://${HOST}:${httpPort}`,
transports: [
{
port: tcpPort,
protocol: 'matt',
scheme: 'tcp',
},
],
...
});

return v.verifyProvider();