Closed omermorad closed 3 years ago
Thanks for this! I’m not really across NestJS or the patterns it uses (I don’t know much about angular), but adaptors to aid adoption are always useful! It’s awesome to see it’s ready for use!
Can you help me understand the value your package adds over using the DSL directly?
You might be interested to take a look at jest pact: https://github.com/pact-foundation/jest-pact - it removes the need for much of the boiler plate. From the looks of the setup in the packages doing, I don’t think you could use it directly, but it might be neat to expose a pactWith with a similar interface for consistency. What do you think?
Thanks for this! I’m not really across NestJS or the patterns it uses (I don’t know much about angular), but adaptors to aid adoption are always useful! It’s awesome to see it’s ready for use!
Haha thanks a lot! 😎
I must say that after some investigation of Consumer Driven Contracts and Contract Testing in general, Pact
is actually something we consider to use in the company!
Can you help me understand the value your package adds over using the DSL directly?
Yes of course. See, Nest is using a DI system almost identical to the one Angular uses, they use Injectable
services, or to be more precise, Injectable
providers.
The two main modules in this package (Consumer
and Provider
) wrap Pact
, Verifier
and Publisher
, so you can inject and use some other services/providers while using them.
Let's say for example that I want to create the Verifier
with some custom options - if I will use the DSL directly, I would not have the ability to inject a Repository
service or even a ConfigService
when creating it:
@Injectable()
export class PactProducerConfigOptionsService
implements PactProducerOptionsFactory {
public constructor(
private readonly animalRepository: AppRepository,
private readonly config: ConfigService,
) {}
public createPactProducerOptions(): PactProducerOptions {
let token;
return {
provider: 'Animal Profile Service',
logLevel: this.config.get('pact.logLevel'),
requestFilter: (req, res, next) => {
console.log(
'Middleware invoked before provider API - injecting Authorization token',
);
req.headers['MY_SPECIAL_HEADER'] = 'my special value';
// e.g. ADD Bearer token
req.headers['authorization'] = `Bearer ${token}`;
next();
},
stateHandlers: {
'Has no animals': async () => {
this.animalRepository.clear();
token = '1234';
return 'Animals removed to the db';
},
'Has some animals': async () => {
token = '1234';
this.animalRepository.importData();
return 'Animals added to the db';
},
'Has an animal with ID 1': async () => {
token = '1234';
this.animalRepository.importData();
return 'Animals added to the db';
},
'is not authenticated': async () => {
token = '';
return 'Invalid bearer token generated';
},
},
// Fetch pacts from broker
pactBrokerUrl: 'https://test.pact.dius.com.au/',
consumerVersionTags: ['prod'],
providerVersionTags: ['prod'],
enablePending: true,
pactBrokerUsername: '****',
pactBrokerPassword: '*****',
publishVerificationResult: true,
providerVersion: '1.0.0',
};
}
}
In the above example you can see the power of injection when creating the Verifier
configurations - I'm using the repository and the config service easily.
It's actually quite difficult to explain it to someone who is not really across NestJS or the patterns it uses, but it gives a a lot of value to using Pact with NestJS.
I would be happy to add a PR that demonstrates the full usage of the package and the real value it gives.
You might be interested to take a look at jest pact: https://github.com/pact-foundation/jest-pact - it removes the need for much of the boiler plate. From the looks of the setup in the packages doing, I don’t think you could use it directly, but it might be neat to expose a pactWith with a similar interface for consistency. What do you think?
Actually I've explored the e2e
example and the Jest
example as well! Unfortunately you are right, I can not use it directly.
pactWith({ consumer: "MyConsumer", provider: "MyProvider" }, (provider) => { ... });
The provider
argument comes from the callback function here, but if you look the the following example you will understand even better why it is not possible (you need to create the provider yourself)
describe('Pact', () => {
let pactFactory: PactFactory;
let provider: Pact;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule, PactModule],
}).compile();
pactFactory = moduleRef.get(PactFactory);
provider = pactFactory.createContractBetween({
consumer: 'Consumer Service',
provider: 'Provider Service',
});
const completeOptions = await provider.setup();
});
afterEach(() => provider.verify());
describe('when a call to send an email from Cats Service is made', () => {
beforeAll(() =>
provider.addInteraction({ ... })
);
it('does something', () => {
return expect(...);
});
});
afterAll(() => provider.finalize());
});
Let me know if you have more questions, I will be happy to answer them and give even more explanation/example if needed!
Merged in #594 :+1:
Hi,
I have recently released a new package that connects Pact's great technology to one of the most powerful frameworks for developing server-side applications in NodeJS and TypeScript - NestJS.
The package (with 98% coverage) offers the use of two types of modules, one for the consumer and one for the provider. The modules are actually wrappers of the pact library and use the original interfaces of PactJS.
I'd love for you to take a look at the project on Github or on NPM's official website.
These days I am working on a pull-request in which I added a new folder called
nestjs
to theexamples
folder. It contains a fully end-to-end example (similar to thee2e
original example) of using each of the modules (consumer and provider) in combination with Jest test runner which is the runner that NestJS guys have chosen to in the framework.Hope to hear from you guys(!) about what you think of the code quality and practices.
Thanks in advance