pact-foundation / pact-js

JS version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
https://pact.io
Other
1.58k stars 342 forks source link

Improve developer experience around consumer pact testing #1173

Open dsteinbach-ep opened 5 months ago

dsteinbach-ep commented 5 months ago

Before making a feature request, I have:

Description

I couldn't find any discussions about this so I apologize if I missed a critical bit of information that tells me why this is a bad idea. Also, this feature request might be better suited for another repo but I'm not sure if other packages have the same limitations im facing with Pact.js, which is...

I feel like certain design choices of Pact creates a bad developer experience which severely limiting Pacts adoption.

Problem

To start off, I believe unit tests are an ideal place to create your consumer pact tests because:

However...

Overall, the fact you have to setup your interactions BEFORE running the mock server seems to be the crux of all these problems.

Possible Solution

First, mock servers are initialized as soon as consumer/provider relationships are defined during Pact initialization. These mock servers should be long running, created in a global unit test setup and destroyed in a global tear down (if they are not already child processes of the test runner). Something like:

jest.config.js

{
   setupFiles: ['<rootDir>/test/initializePact.ts'],
}

test/pacts.ts

export const productServicePact = new PactV4({
  consumer: 'user-service',
  provider: 'product-service',
});

export const orderServicePact = new PactV4({
  consumer: 'user-service',
  provider: 'order-service',
});

test/initializePact.ts

import { produtServicePact, orderServicePact } from './pacts';

productServicePact.startServer((url) => {
   process.env.PRODUCT_SERVICE_URL = url;
});

orderServicePact.startServer((url) => {
   process.env.ORDER_SERVICE_URL = url;
});

Secondly, interactions (request/response pairs) should be defined per test case and automatically cleared before the next case is ran (writing contract files as it goes). Something like:

it('verifies some state', () => {
   withInteractions([orderServiceInteraction1, productServiceInteraction1, productServiceInteraction2], async () => {
      const userId = 123;
      const response = await productService.getRecommended(userId);
      ... etc ...
      expect(...).toBe(true);
   });
});

With this we wouldn't need dedicated Pact-only unit or e2e tests. It would just be part of how we already mock server request/responses. The time penalty would be minimal and be a completely ergonomic developer experience.

Just my thoughts. I'm pretty new to Pact testing and I am really excited by the value it offers. Hopefully, this wasn't me just completely missing the Pact's intended use. Thanks!

dsteinbach-ep commented 5 months ago

I think I failed to explain above that I believe Pact tests could replace traditional Jest mocking, which means Pact tests and traditional unit tests would not need to be defined separately. Less code to write and the code you are writing delivers more value.

mefellows commented 4 months ago

Hi @dsteinbach-ep, apologies for the delay in responding - I saw this and there was a lot to it. I've not yet had a chance to properly review it, but rest assured a response is forthcoming and also that it is much appreciated - thanks for sharing!