Hi :wave: , we are thinking of adding Pact to our ...
# general
d
Hi šŸ‘‹ , we are thinking of adding Pact to our tests. We have one concern about running the provider's verification. Currently, we are running our services locally in isolation by binding the application interfaces to mocks using IoC. Now, if we want to start using Pact we will have two mock systems, one for tests, and one for an isolated local run. I want to be able to configure and run the Pact mock server locally when running the application. --or-- Use the existing mocked implementations to configure the expected interactions in the tests. Is that possible? Do you have any idea on how to do that correctly? I opened a discussion in GitHub with more details: https://github.com/pact-foundation/pact-js/discussions/887 What do you think about this concern? How do you run your app locally in isolation?
Any thoughts?
b
There's a lot to unpack here. The example looks like a consumer-side scenario, but your explanation talks about provider verification šŸ¤”
Since V3 or V4, you can include state with parameters in the consumer side of the tests, so if you want to use those to set up your provider state (values for test doubles), you can šŸ™‚
t
I don't think I understand your question, sorry.
What is an isolated local run if not for tests?
What is IoC?
What do you mean by configuring the expectations with mocks? That definitely doesn't sound right
The diagrams here might help understand how pact works: https://docs.pact.io/getting_started/how_pact_works
you set up the interactions manually, as expectations.
The answer to your question - whether it is possible to run a pact mock server without a test - is yes. But, I think there's a misunderstanding here about what pact is for
ā˜ļø 1
b
I had to look at the sample code to work out the inversion of control specifics, it's just a DI framework binding.
šŸ‘ 1
t
Oh right. I think I understand the question - you're saying that you have a pact verification for your service's endpoints, and your service makes some downstream service calls, which you would usually mock during testing. But, if you also test those downstream service calls with pact, then you're thinking "maybe we can just reuse what we set up for pact instead of manually maintaining mocks"?
šŸ’” 1
You could do this I suppose. I agree with you that it's a lot of mucking around. I don't really think of pact as a mock service (it happens to be backed by one, but the real killer feature is the ability to record and share the contract)
An advantage of using mocks like your example (which is what I would do) is that they are very fast - all your unit tests don't hit the network boundary
You still want to tie your mock together with your pact expectation, of course
I spoke about this a bit last year, let me get you a link

https://youtu.be/wkld_wRsTDE?t=2545ā–¾

This is the slide - although that section of the talk starts earlier.
I configure the mocks with the pact expectations, not the other way around
I don't think of this as double-mocking - the pact test is around the client in your code - asserting that the innards of
getOrder(id: string): Promise<Order>;
work correctly and return what you expect.
Pact mocks the network part, and what is under test is everything inside
getOrder
Now, for your unit tests, you know you can safely mock everything inside
getOrder
For maximum safety, you can use the pact expected return as the mock data that is returned by mock
getOrder
d
Thanks @Timothy Jones! That was really insightful, few thoughts... Seems like I better understand now the benefits of adding Pact, it can validate that my mock classes mimics the provider correctly. Which improves the confidence on my unit tests, it also gives us coverage on the live concrete implementation. As you said in the slide, there is some overlap between functional tests and contract tests, which is good because it validates that isolated run and production run works the same. Just need to find the balance. This is stronger than traditional integration tests where your mocks and live implementations are separated. To maintain this overlap correctly, I need that one of these will happen: 1) Mocks will use Pact 2) Pact will use mocks 3) Pact and mocks will use another component that maintains mocked data. I feel like option 2 suites us best. If we will also implement a state handler, we need it to dynamically mutate our mocks from outside. Our mocked classes already has this possibility and it really fits our needs.
t
Option 2 might be easiest at first, but you may have a bad time long term. The reason for this is that to get the best use from pact, you'll want to use matchers
For example, your contract for a user might look like:
Copy code
{
  "firstName": "Dor"
}
But maybe that's not the exact mock data that is on the provider. And, in the contract test, you don't really care exactly what the string returned from the provider is, just that it really was a string
so, you then use matchers:
Copy code
{
      "firstName": like("Dor")
}
This means your consumer contract test can expect an explicit string, and your provider verification will pass as long as the provider returns any string (eg
{"firstName": "Tim"}
)
d
Hmmm interesting, haven't thought about the matchers, but if I pass
like(mock.getOrder)
it will have similar result? It doesnt give us flexibility of using the matcher
So it seems like option 3 is the best, but it requires more to maintain
b
You should definitely think about flexible matchers šŸ™‚ they're what gets Pact to the sweet spot of "flexible like schemas" but with "the reasoning power of unit tests".
like(mock.getOrder)
will probably get you close to schema-level flexibility & reasoning. It doesn't let you specify which parts of the payload are important in value vs shape/type.
t
The primary downside of
like(wholePayload)
is that you will only match on the json types of everything. If your payloads are simple then you can totally get away with it. However, it won't work for all payloads, so you might miss things: Cases where this won't work include: • where the client only understands specific values where you need an enum (say
userType
which could be
"admin"
or
"member"
- this is an important part of the contract, because you want to be sure that the backend doesn't return
"someOtherType"
or maybe "ADMIN" if your client is case sensitive) . • If you have any arrays, it will tie the backend to providing exactly that structure • Same thing you have any objects used as maps
šŸ‘ 1
d
Thanks! So I can take the mock result
mock.getOrder(id)
and translate it to a Pact response body more precisely according to the test, something like this:
Copy code
const order = mock.getOrder(id);
const expectedResponseBody: Order = {
    id,
    name: like(order.name),
    // ...
}
So my pact tests can use my mocks, with the flexibility of using matchers. Right now, this seems the easiest way to manage the mocks in our services. WDYT?
t
Yes, exactly.
Once you have that object with matchers, you can use the stripMatchers function to get back the raw object if you need- then you can import it in your other mocks
šŸ‘ 1
(It may not be called stripMatchers, I'm not at my computer)
d
Thank you! @Timothy Jones