Skip to content

Instantly share code, notes, and snippets.

@bethesque bethesque/index.md
Last active Jul 31, 2019

Embed
What would you like to do?
Using pact with providers that have their own providers

Eg. A -> B -> C

A more find grained view of this intergration might go:

|------------------A Codebase------------------||------------------B Codebase------------------||--------C Codebase---...
 A UI -> A Controller -> B Service -> B Client -> B API -> B Controller -> CService -> CClient -> C API etc...  
                                           |---A/B pact------------------------?
                                           |---A/B pact------------------------------------?
                                           |---A/B pact----------------------------------------------?

The recommended approach for creating the A/B Pact is to write Pact tests for the BClient class (testing the "back door" of A and the "front door" of B). When you verify the A/B Pact, it's best to stub out the calls to C at some level. You may choose to stub at the Service, Client or API (HTTP) level. Which ever level you choose, you then need to make sure that you have another test that ensures that the real compontent behaves the same way as the stubbed component, just like Pact all over again. You'll need some sort of "contract" to keep those tests in sync, whether that contract is another Pact, or a "shared artifact" (more on that below).

1. Stub the C client or service class

Assuming that all your interactions between B and C go through a "CClient" class, you can create a stub implementation of CClient that allows you to set arbitrary responses during your provider state set up call.

class CClientStubImpl implements CClient {
  
  private Alligator alligator;


  public void stubAlligator(Alligator alligator) {
    this.alligator = alligator;
  }

  @Override
  public Alligator getAlligator(String name) {
    return alligator;
  }

}

class CStubbedObjects {
  public static final Alligator MARY = new Alligator("Mary");

}

During provider state set up:

cclient.stubAlligator(CStubbedObjects.MARY);


Then, in your Pact test for CClient, you can use the same alligator instance as a "link" to tie your stubbing to an artifact that will get verified.

Ron can you give me an example for pact jvm or groovy?

2. Use the pact that you have generated between B and C to create a stub server

A pact is best created using unit tests for the client class. Ideally, you could have a CClient in your B codebase, and you would write your Pact tests using that CClient class.

Once you have generated that pact in the B unit tests, it can than be used with one of the Pact stub server implementations to run up an HTTP mock during the A/B verification.

One thing to beware of with this approach is that you'll want to use the type/regular expression based matching (the "flexible" matching) when you set up your expected requests in your B/C Pact tests. Usually, we would advise that your request expectations use exact values, as you're in full control of the HTTP request that is sent to the mock server in a unit test. However, if your outgoing request to C is being generated from your incoming request from A during a Pact verification, you can't control what is going to be in that request, so you'll want to be as flexible as possible.

@rholshausen

This comment has been minimized.

Copy link

rholshausen commented May 10, 2018

Do you need me to provide an example of using a mock client, or an example of a "link" to tie your stubbing to an artifact that will get verified?

I haven't had much success with using option 2. The problem is the data used in the Pact between B and C could be very different as from that from A to B. And you want to avoid coupling your tests together with common data.

@TimothyJones

This comment has been minimized.

Copy link

TimothyJones commented Jun 17, 2018

Wouldn't it be better to use the Pact state change to stub the B controller? I thought the general pact strategy would be the same regardless of whether B needs a provider or not - this has the advantage that it doesn't matter how many providers there are in the chain.

A->B Pact tests cover B Service -> B Client -> B API
B's unit tests: cover B Controller
B->C Pact tests cover C Service -> C Client -> C API
C's unit tests cover C Controller
(...etc)

This might be a misuse of the word "controller", depending on the model - but I mean the domain layer that the API calls. For a passthrough API, it will be super thin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.