Hi all :wave: Is there a way to add multi interac...
# pact-js
j
Hi all đź‘‹ Is there a way to add multi interactions to a provider for POST and GET calls but on the same path without overriding the contracts? Thank you
m
Yes, they need unique descriptions
j
Hi Matt as in I need to create two different providers for the two calls? and can they be on the same port number?
m
No, each interaction has a description. That should be unique to avoid overwriting
j
I see thanks Matt. I have few other questions if that is okay 🙂 1. I am occasionally getting some flakiness when running the tests. sometimes it passes but sometimes I am getting a cannot find a matching request error, why that would be? 👀 2. When validating the status code being returned, I can console log it correctly, but when I validate it in the test
expect(response.status).to.equal(400)
That still passes đź‘€ even though if I log status that is 204 3. I am occasionally getting an error
Error: Port 8080 is unavailable on address ...
why would that be? Thanks
👍 1
m
That all sounds of mishandled promises and/or lifecycle configuration (i.e. not shutting down the pact server correctly after tests and it's hanging around, so you get a poet conflict). Feel free to share your consumer test and we can see
Also see the troubleshooting / debugging guides in the readme
(also #2 tells me your actual test assertion is wrong). If the test passes, that's your code not pacts)
j
Thanks Matt
Copy code
describe('sample test', () => {
    before(() =>
        provider.setup().then(() =>
            provider.addInteraction({
                state: 'GET success',
                uponReceiving: 'GET',
                withRequest: {
                    method: 'GET',
                    path: '/get',
                    headers: {
                        'Accept': 'application/json; v=1',
                        'Content-Type': 'application/json; v=1',
                    }
                },
                willRespondWith: {
                    status: 200,
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: eachLike(expectedBody)
                },
                state: 'PATCH success',
                uponReceiving: 'PATCH',
                withRequest: {
                    method: 'PATCH',
                    path: '/patch',
                    headers: {
                        'Accept': 'application/json; v=1',
                        'Content-Type': 'application/json; v=1'
                    },
                    data: {
                        value: 'xyz'
                    }
                },
                willRespondWith: {
                    status: 204,
                    headers: {
                        'Content-Type': 'application/json'
                    }
                }
            })
        )
    );

    it('should return the expected response body', () => {
        axios
            .request({
                method: 'GET',
                url: '<http://localhost:3000/get>',
                headers: {
                    'Accept': 'application/json; v=1',
                    'Content-Type': 'application/json; v=1',
                }
            })
            .then(response => {
                expect(response.data).to.equal(expectedBody);
            });
    });

    it('should return the expected response body', () => {
        axios
            .request({
                method: 'PATCH',
                url: '<http://localhost:3000/patch>',
                headers: {
                    'Accept': 'application/json; v=1',
                    'Content-Type': 'application/json; v=1'
                },
                data: {
                    value: 'xyz'
                }
            })
            .then(response => {
                expect(response.status).to.deep.equal(204);
            });
    });

    afterEach(() => provider.verify());
    after(() => provider.finalize());
});
This is the code I have written
m
so your
it
blocks aren’t returning or awaiting promises - this will be the reason for the flakey tests
It’s also the reason why
expect(response.status).to.deep.equal(204)
is not causing your test to fail - the
it
block has returned, so it can’t change the outcome
Lastly, this is not doing what you think it does:
Copy code
provider.addInteraction({
                state: 'GET success',
                uponReceiving: 'GET',
                withRequest: {
                    method: 'GET',
                    path: '/get',
                    headers: {
                        'Accept': 'application/json; v=1',
                        'Content-Type': 'application/json; v=1',
                    }
                },
                willRespondWith: {
                    status: 200,
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: eachLike(expectedBody)
                },
                state: 'PATCH success',
                uponReceiving: 'PATCH',
                withRequest: {
                    method: 'PATCH',
                    path: '/patch',
                    headers: {
                        'Accept': 'application/json; v=1',
                        'Content-Type': 'application/json; v=1'
                    },
                    data: {
                        value: 'xyz'
                    }
                },
                willRespondWith: {
                    status: 204,
                    headers: {
                        'Content-Type': 'application/json'
                    }
                }
            })
You’re passing a JSON object to
addInteraction
. The duplicate keys are conflicting - only one of the
withRequest
,
state
etc. will actually win.
You need to call
addInteraction
per
it
block, not line them all up in a single
before
block
y
Hey hey, As noted by Matt, you would want to add an interaction per test. You can see an example in our example-consumer project We setup two tests here, both using the same
path
but a different
id
. https://github.com/pactflow/example-consumer/blob/d4777a53d42f7688a7c5586c3296c08cdf1f08f4/src/api.pact.spec.js#L25 https://github.com/pactflow/example-consumer/blob/d4777a53d42f7688a7c5586c3296c08cdf1f08f4/src/api.pact.spec.js#L55 You can register multiple interactions per mock server, but each
addInteraction
requires a single object, not the dual objects you have passed in. If you use TypeScript you can get intelligent typing support. Also assuming your example is just for illustration purposes, are the client under test, isn't your application code, but a client constructed during the test (to make the call to the mock provider directly)
👍 1
j
Thanks both 🙂 Hi Matt, when you said that my
it
block isn’t returning a promise. what do you mean? 👀
m
Copy code
it('should return the expected response body', () => {
        // this next line is a promise, but you're not returning it or await-ing it. This means the it block immediately returns before the promise does it's work. 
        axios
            .request({
                method: 'GET',
                url: '<http://localhost:3000/get>',
                headers: {
                    'Accept': 'application/json; v=1',
                    'Content-Type': 'application/json; v=1',
                }
            })
            .then(response => {
                expect(response.data).to.equal(expectedBody);
            });
    });
You should look up “javascript promises” to read more on what this means, as well as the async/await syntax. I’d highly suggest pairing with somebody that knows JS to write Pact tests. Pact is a unit testing framework, so you need good understanding of how to write good, isolated unit tests and be comfortable writing them in a framework like Mocha/Jest/chai before even using Pact.
With respect, the errors in the code above shows a lack of some basic understanding of JS, so even if I directly get your tests passing, I fear that you’ll stumble at the next block without a deeper understanding of those basics
j
Thanks Matt, I will take a look at promises again, but the port unavailable error, I don’t think that is JS or promise related
👍 1
I have the test working now, but the contract keeps overriding, is that expected?
m
the port unavailable error, I don’t think that is JS or promise related
this can happen if the tests aren’t shutting down properly or if
finalize()
isn’t called. You should be able to see if the server is hanging around (it’s a Ruby process so
ps -ef | grep ruby
will usually show any hanging processes.
I would suggest you don’t use a fixed port, and instead let the framework allocate one for you. You simply omit the port, and it will be returned in the response to
provider.setup()
. Most of our examples use this format
I have the test working now, but the contract keeps overriding, is that expected?
Each time the test suite is run, it should replace the contents of the contract, yes. What were you expecting?
j
I should have explained it. So if we create a server
y
are you running multiple tests in parallel? https://github.com/pact-foundation/pact-js#parallel-tests we have some examples here.
j
mock provider with multiple interactions, should it add both interactions rather than replacing with the latest one?
or am I missing something?
why is it recommended to use fixed port?
m
why is it recommended to use fixed port?
I think you mean “not recommended”. The reason is, port conflicts with other tools on your (and your colleagues) machines. If you don’t specify a port, we’ll allocate a free one. If you always use a fixed port, it might conflict with another tool down the track
âś… 1
mock provider with multiple interactions, should it add both interactions rather than replacing with the latest one?
It should add both
But as per above, you need to call
addInteraction
for each interaction you want to test (usually a 1:1 between
addInteraction
and
it
blocks)
j
I did call addInteraction for each interaction as suggested
y
Are you able to share your example code in GitHub @Jiayao Xu?
j
I managed to fix this, thanks, this is on a enterprise GitHub for PoC, so I cannot share the code
👍 1
✌️ 1