Build Status

Using Arquillian Algeron Pact Provider requires at least Arquillian Core 1.1.12.Final or above.

1. What is Arquillian Algeron?

In microservices architecture you typically have one or more services that make remote calls to one or more services to get information from them.

For example you might have one service called BeerService that returns information of a beer. But this service needs to call another service CommentsService which returns the comments that users has done for given beer. After BeerService receives all the comments for that beer, everything is packed and sent to the client.

So as you can see there is a communication between both services where one needs to know what and how to send data such as name of fields, type of data or status code (CommentsService) and another one that needs to know how to interpret what is received (BeerService).

It seems obvious that it must exist some kind of agreement between them so they can understand each other correctly in all of their communications.

2. Consumer Driven Contracts

A Contract is a collection of agreements between a client ( or Consumer which in previous example is BeerService) and an API (or Provider which in previous example is CommentsService) that describes the interactions that can take place between them.

In summary Consumer Driven Contracts is a pattern that drives the development of the Provider from its Consumers point of view. This means that consumer describes what and how he wants to receive the information, describing in form of contract and then provider implements its service following that contract. When the client validates that can consume what it is been defined in the contract and provider validates that what he produces meets the contract, then you can be sure that contract expectations are met and they will be able to communicate each other.

Also notice that thanks of these tests, if anyone changes the client or the server side to not meet the contract, you’ll detect before it is deployed to production.

You can read more about Consumer-Driven contracts here or at book Testing Java Microservices chapter 6.

3. Pact

3.1. Arquillian Algeron Pact

Currently Arquillian Algeron only supports one format of contracts (Pact). But we have plans to extend it to other formats like swagger.

Pact-JVM is an implementation for JVM languages of Pact.

Arquillian Algeron Pact is the integration of all Arquillian philosophy/extensions into Consumer Driven Contracts approach using Pact-JVM.

Pact is a framework that provides support for Consumer Driven Contracts testing. Pact is implemented in several languages such as Ruby, JVM, .NET, JavaScript, Go and Swift.

3.1.1. How Pact works?

Obviously any Contract test is composed by two faces, one for the consumer and another one for the provider and a contract file that is sent from consumer to provider. Let’s see the steps to write contract tests using Pact.

  1. Consumer expectations are set up on a mock server. In this way consumer doesn’t need to know how to deploy a provider (since it might not be trivial to do it, and probably at the end you’ll end up writing end-to-end tests instead of contract tests). So consumer tests its client/gateway code to communicate against a mock server.

  2. When consumer tests are run, mocked requests and responses are written into a "pact" file, which in fact it is the contract that has been defined in consumer part.

  3. The "pact" file are sent to provider project to be replayed against the provider service. Then real responses from provider are checked against the expected responses defined in contract.

  4. If consumer is able to produce a "pact" file and provider meets all the expectations, then you can say that contract is verified by both parties and will be able to communicate.

These steps can be summarized in next diagram:

pact two parts

4. Arquillian Algeron Consumer

4.1. Arquillian Algeron Pact Consumer

First thing to do is develop the Consumer part of the test. Consumer part of Consumer-Driven Contract defines requirements from the consumer of the API which are then used to develop the client interaction with the API as well as to validate provider implementation.

4.1.1. Pact Consumer Enrichers

You can use in a test @StubServer annotation to inject URL where stub http server is started.

@RunWith(Arquillian.class)
@Pact(provider="test_provider", consumer="test_consumer")
public class MyTest {

  @StubServer
  URL url;

}

4.1.2. Pact Consumer In Container

Arquillian has two modes of working In Container (which means having a @Deployment method) and Arquillian takes care of managing lifecycle of the server and deploy the archive, or Standalone (no @Deployment method) where Arquillian assumes that everything is already deployed.

Let’s see how to write the consumer part in In Container mode.

First thing to do is add typical Arquillian dependencies such as Arquillian bom and the dependency of the container you want to use. Nothing new here:

pom.xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>${version.arquillian_core}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.jboss.arquillian.junit</groupId>
        <artifactId>arquillian-junit-container</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.arquillian.container</groupId>
        <artifactId>arquillian-container-chameleon</artifactId> (1)
        <version>1.0.0.Alpha7</version>
        <scope>test</scope>
    </dependency>
</dependencies>
1 Notice that I am using Chameleon generic container, you can read more here.

Then you need to add arquillian-pact-consumer dependency as well as pact-jvm-consumer dependency:

<dependencies>
    <dependency>
        <groupId>org.arquillian.algeron</groupId>
        <artifactId>arquillian-algeron-pact-consumer-core</artifactId>
        <version>${version.arquillian_algeron}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>au.com.dius</groupId>
        <artifactId>pact-jvm-consumer_2.11</artifactId>
        <scope>test</scope>
        <version>3.5.0-beta.2</version> (1)
    </dependency>
</dependencies>
1 Arquillian Algeron Pact has been tested with latest version, but it should work with any 3.X version.

After dependencies you can write the test that defines the contract:

ClientGatewayTest.java
@RunWith(Arquillian.class) (1)
@Pact(provider="test_provider", consumer="test_consumer") (2)
public class ClientGatewayTest {

    @Deployment (3)
    public static JavaArchive createDeployment() {
        return ShrinkWrap.create(JavaArchive.class).addClasses(ClientGateway.class);
    }

    public PactFragment createFragment(PactDslWithProvider builder) {

        Map<String, String> header = new HashMap<>();
        header.put("Content-Type", "application/json");

        return builder
                .given("test state")
                .uponReceiving("ConsumerTest test interaction")
                .path("/")
                .method("GET")
                .willRespondWith()
                .status(200)
                .headers(header)
                .body("{\"responsetest\": true, \"name\": \"harry\"}")
                .toFragment(); (4)
    }

    @Inject (5)
    ClientGateway clientGateway;

    @StubServer (6)
    URL url;

    @Test
    @PactVerification("test_provider") (7)
    public void should_return_message() throws IOException {
        assertThat(clientGateway.getMessage(url), is("{\"responsetest\": true, \"name\": \"harry\"}")); (8)
    }
}
1 Arquillian Runner
2 To define the contract you need to configure expectations of mock server. This is done by annotating a method or class with @Pact annotation and setting the provider and consumer name
3 Defines what you want to deploy to defined container. In this case an EJB that acts as gateway to another service
4 A Pact method returns a fragment of the contract which might be the complete contract or not
5 Typical Arquillian enrichment
6 @StubServer annotation is used for enrich test with stub http server Url.
7 Defines which provider is validated when this test method is executed.
8 Asserts that the gateway can read the kind of messages sent by provider. Notice that now it is pretty simple, but in real test you’ll test for example that message is correctly bound to an object.

After this test is executed, contract is placed at target/pacts directory or build/pacts in case of using Gradle. Then you can send contract to provider side.

Notice that contract and/or mock responses are defined using Pact DSL. You can read more about this DSL at Using the Pact DSL directly.

Important things to retain from consumer part:

  • It is an Arquillian test so you can use anything that Arquillian provides.

  • Methods or classes annotated with @Pact defines a fragment of the contract that at same time is used by mock server for providing responses.

  • In case of using several methods annotated with @Pact annotation, you need to use fragment attribute @PactVerification(.. fragment="methodName")`to set which fragment is under test for each `@Test method.

You can use @Pact annotation at class level, so methods that defines a pact fragment, just need to return PactFragment class. Information will be picked from class level. In case of setting annotation at class and method level too, the one defined on the method will take precedence.

You can see the full example at: ftest-incontainer

4.1.3. Pact Consumer Standalone

Standalone mode (no @Deployment method), Arquillian assumes that everything is already deployed.

The only difference between previous example is that you only need arquillian-junit-standalone instead of arquilian-junit-container and don’t need to define any container adapter. Obviously no @Deployment method is required:

ConsumerTest.java
@RunWith(Arquillian.class)
public class ConsumerTest {

    @Pact(provider = "test_provider", consumer = "test_consumer")
    public PactFragment createFragment(PactDslWithProvider builder) {

        Map<String, String> header = new HashMap<>();
        header.put("Content-Type", "application/json");

        return builder
                .given("test state")
                .uponReceiving("ConsumerTest test interaction")
                .path("/")
                .method("GET")
                .willRespondWith()
                .status(200)
                .headers(header)
                .body("{\"responsetest\": true, \"name\": \"harry\"}")
                .toFragment();
    }

    @StubServer
    URL url;

    @Test
    @PactVerification("test_provider")
    public void runTest() throws IOException {
        new ConsumerClient().get(url).body("name", equalTo("harry"));
        assertThat(new File("target/pacts/test_consumer-test_provider.json")).exists();
    }

}

Code is pretty similar as previous test but now the consumer client/gateway object is instantiated as plain java object instead of container managed.

You can see the full example at: ftest

4.1.4. Pact Consumer Configuration

Arquillian Algeron Pact Consumer can be configured using Arquillian configuration mechanism (arquillian.xml file or system properties).

arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://jboss.org/schema/arquillian"
            xsi:schemaLocation="http://jboss.org/schema/arquillian
    http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <extension qualifier="pact-consumer">
        <property name="pactSpecVersion">1</property>
    </extension>

</arquillian>

The attributes are:

Attribute Name Description Default Value

host

Bind host for mock server

localhost

port

Listening port for mock server

9090

pactSpecVesion

Pact specification version as integer

3

https

If mock server should start using https instead of http

false

provider

If you want to set provider globally

pactArtifactVersion

By default Arquillian Algeron Pact packages pact dependencies in container tests. To get the version of artifacts to package, checks the current pact version of classpath. If you want to use another one rather the one defined in your classpath, you can set it here the version.

pactReportDir

Directory where contracts are stored.

target/pacts

4.1.5. JBoss Forge Arquillian Addon

Forge Arquillian Addon offers an integration with Arquillian Algeron Consumer.

To use it apart from having the Forge Arquillian Addon installed in Forge, you need to have arquillian dependencies registered on build tool.

To register Arquillian Consumer dependencies in build tool just run next command:

arquillian-algeron-setup-consumer --contracts-library pact

After that you can enrich a given Arquillian test with consumer annotations and contract scaffolding with next command:

arquillian-algeron-create-contract-fragment --consumer myconsumer --provider myprovider --fragment myFragment --test-class org.superbiz.MyContractTest

It is important to note that test class should be already created (for example using arquillian-create-test --named MyContractTest --target-package org.superbiz command).

See it alive in next terminal cast:

104383

4.2. Arquillian Algeron Consumer Publisher

Arquillian Algeron also offers additional ways to of publishing contracts files comparing to what Pact itself is providing.

In Arquillian Algeron we have defined a Pact Publishing SPI so you can implement your own publisher. We currently support three different publishers - Folder, URL[POST method] and Git.

It is important to note that by default publishContracts configuration attribute is false. This means that when you run any consumer contract test, contracts are not published. publishContracts configuration attribute should be only set to `true`if and only if you are publishing a new version of a consumer, and this will be done by your CI/CD environment.

Arquillian can be configured using system properties or environment variables. If you want to enable pact publishing feature only in CI/CD, you can set environment variable arq.extension.algeron-consumer.publishContracts to true. Also you can use the form <property name="publishContracts">${env.publishcontracts:false}</property> and setting publishContracts environment variable with correct value.

4.2.1. Folder Publisher

Folder publisher copies "pact" files from configured output directory (by default target/pacts) to another directory. To configure folder publisher you need to configure pactPublishConfiguration with next configuration:

arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://jboss.org/schema/arquillian"
            xsi:schemaLocation="http://jboss.org/schema/arquillian
    http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <extension qualifier="algeron-consumer">
        <property name="publishConfiguration">
            provider: folder (1)
            outputFolder: /mypacts (2)
            contractsFolder: target/pacts (3)
        </property>
    </extension>

</arquillian>
1 provider attribute is used for setting which publisher to use. In case of Folder publisher, you need to set to folder.
2 outputFolder configures where to copy contract files.
3 contractsFolder configures folder where contract files are generated by the engine.

You can set outputFolder value using Java system property ${name:defaultvalue} or environment variable `${env.name:defaultvalue}. For example `outputFolder: ${output:/mypacts} will first check if there is a Java system property with name output and get the value. If that is not defined it will use the default value i.e. /mypacts.

4.2.2. URL Publisher

URL publisher sends a POST request to configured URL, appending at the end of the URL the "pact" filename and sending the contract content as body content. For example given http://myhost/pacts and a "contract" file called consumer_provider.json, the resulting URL would be: http://myhost/pacts/consumer_provider.json

arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://jboss.org/schema/arquillian"
            xsi:schemaLocation="http://jboss.org/schema/arquillian
    http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <extension qualifier="algeron-consumer">
        <property name="publishConfiguration">
            provider: url (1)
            url: http://localhost:8081/pacts (2)
            contractsFolder: target/pacts (3)
        </property>
    </extension>

</arquillian>
1 provider attribute is used for setting which publisher to use. In case of URL publisher, you need to set to url.
2 url configures to send as POST the contract content.
3 contractsFolder configures folder where contract files are generated by the engine.

You can set url value using Java system property ${name:defaultvalue} or environment variable ${env.name:defaultvalue}.

4.2.3. Pact Broker

You can publish contracts to Pact Broker server.

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: pactbroker
          url: <url of pact broker server>
          username: <username to access>
          password: <password to access>
          contractsFolder: <path where pact files are generated>
          version: <version which pacts are stored>
  </property>
</extension>

Also you need to add org.arquillian.algeron:arquillian-algeron-consumer-pact-broker-publisher:${version} dependency. This is only supported in case of using Pact provider.

Possible attributes:

Parameter Description

url

Mandatory field that sets url of Pact Broker.

contractsFolder

Mandatory field that sets the location of Pact files.

username

Optional field that sets the username.

password

Optional field that sets the password.

version

Optional field that sets the version of contracts in pact broker. If not set latest is used.

Notice that all these attributes can be set using system properties or environment variable as any other property in arquillian.xml.

4.2.4. Git Publisher

Git publisher publishes contract files to a git repository. Optionally they can be committed into a branch or tag the commit.

This publisher just takes the generated contract files, copied to repository, commit them and push them to remote.

First of all you need to add git publisher dependency:

pom.xml
<dependency>
    <groupId>org.arquillian.algeron</groupId>
    <artifactId>arquillian-algeron-consumer-git-publisher</artifactId>
</dependency>
arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://jboss.org/schema/arquillian"
            xsi:schemaLocation="http://jboss.org/schema/arquillian
    http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <extension qualifier="algeron-consumer">
        <property name="publishConfiguration">
            provider: git (1)
            url: https://localhost:8080/contracts (2)
            comment: This is a new version of contracts (3)
            username: admin (4)
            password: admin
            passphrase: aaaa (5)
            key: ~/mykey (6)
            remote: origin (7)
            repository: /git/myrepo (8)
            contractGitDirectory: pacts/ (9)
            tag: v ${version:1.0.0-SNAPSHOT} (10)
            branch: master (11)
            email: my@email.com (12)
            contractsFolder: target/pacts (13)
        </property>
    </extension>

</arquillian>
1 provider attribute is used for setting which publisher to use. In case of Git publisher, you need to set to git.
2 url sets the git repository. This is mandatory field.
3 comment set comment message. This is mandatory field.
4 username and password for accessing repository.
5 passphrase to access to private key.
6 key private key location, by default ~/.ssh/id_rsa.
7 remote repository. By default origin.
8 repository sets location of repository. If it is an empty directory, git repository is cloned there. If it is already a git repository, a git pull operation is executed. By default a temp directory is created.
9 pactDirectory configures where pact files are stored inside repository. By default gets root directory.
10 tag is used for tagging commit done with new pact files.
11 branch sets a branch where contract files are copied and committed. By default is master.
12 email used for commit. By default it gets email from general configuration.
13 contractsFolder configures folder where contract files are generated by the engine.

Any of the git attributes can be set using Java system property ${name:defaultvalue} or environment variable ${env.name:defaultvalue}.

4.2.5. SPI

You can also implement your own publisher. To make it so you need create a class that implements org.arquillian.algeron.consumer.spi.publisher.ContractsPublisher and register this service inside META-INF/services/org.arquillian.algeron.consumer.spi.publisher.ContractsPublisher.

4.2.6. JBoss Forge Arquillian Addon

Forge Arquillian Addon offers an integration with Arquillian Algeron Publishers.

To use it apart from having the Forge Arquillian Addon installed in Forge, you also need to have Arquillian Algeron Consumer dependencies registered on build tool (for example using command arquillian-algeron-setup-consumer --contracts-library pact JBoss Forge Arquillian Consumer Addon ).

Each of the publishers have its own command to be registered where you can set its specific configuration parameters.

folder

arquillian-algeron-setup-publisher --publisher folder --output-folder /tmp/pacts

url

arquillian-algeron-setup-publisher --publisher url --url http://localhost

git

arquillian-algeron-setup-publisher --publisher git --url http://localhost --comment newcomment

See it alive in next terminal cast:

104386

5. Arquillian Algeron Provider

5.1. Retrievers

5.1.1. Retrievers as Annotation

One real important thing in provider tests is where contract files are located and how the test should load it. To specify this, Arquillian Provider provides some class annotations, but of course you can implement your own as well:

From URL

@ContractsUrl(urls = {"http://build.server/zoo_app-animal_service.json"} )

From PactBroker

@PactBroker(url="${pactbroker.hostname:localhost}") Notice that in this case system properties with defaults are supported. Also you need to add org.arquillian.algeron:arquillian-algeron-pact-provider-pact-broker-loader:${version} dependency. This is only supported in case of using Pact provider.

From Folder

@ContractsFolder("subfolder/in/resource/directory") You can use absolute or relative path. The loader first checks if resource is in classpath and if not checks directory. As with PactBroker, you can use system properties.

From Maven

@ContractsMavenDependency(value = "org.superbiz:contract:[1.0,]"). Also you need to add org.arquillian.algeron:arquillian-algeron-provider-maven-retriever:${version} dependency. In case of specifying a range of versions, this loader will take always the highest one. You can use system properties to set values too.

From Git

@ContractsGit("https://github.com/lordofthejars/starwarspact.git") Also you need to add org.arquillian.algeron:arquillian-algeron-provider-git-retriever:${version} dependency. You can use system properties to set vales too. Inspect @ContractsGit annotation for all options that accept this loader such as authentication, use branch or tag or set a pact directory.

It’s possible to use a custom Contracts retriever. For this, implement interface ContractsRetriever and annotate the test class with @ContractSource(MyOwnRetriever.class).

class MyOwnRetriever must have a default empty constructor or a constructor with one argument of class Class which at runtime will be the test class so you can get custom annotations of test class.

You can see an example at: ContractsUrlLoader java class.

5.1.2. Retrievers as Configuration

As it happens with consumer part, Arquillian Algeron allows you to configure retrievers in arquillian.xml.

Folder Retriever

You can retrieve contracts from folder:

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: folder
          contractsFolder: <folder where contracts are stored>
    </property>
</extension>
URL Retriever

You can retrieve contracts from URL/s.

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: url
          url: url where contract is stored (1)
    </property>
</extension>
1 url where contracts are stored (this can be in form of string or yml list)
Git Retriever

You can retrieve contracts from Git repository.

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: git
          url: <giturl>
          username: username
          password: password
  </property>
</extension>

Also you need to add org.arquillian.algeron:arquillian-algeron-provider-git-retriever:${version} dependency.

Possible attributes:

Parameter Description

url

Mandatory field that sets git server url.

username

Optional field that sets the username to access to git repository.

password

Optional field that sets the password to access to git repository.

passphrase

Optional field that sets the passphrase to open the private key.

remote

Optional field that sets the remote name (algeron uses origin by default)

key

Optional field that sets the private key location. (Algeron uses ~/.ssh/id_rsa by default)

repository

Optional field that sets local location of git repository.

contractGitRepository

Optional field that sets inner folder where contracts are stored.

tag

Optional field that checkouts given tag

branch

Optional field that checkouts given branch

Notice that all these attributes can be set using system properties or environment variable as any other property in arquillian.xml such as:

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: git
          url: <giturl>
          username: username
          password: ${env.username}
  </property>
</extension>
Maven Retriever

You can retrieve contracts from Maven artifacts.

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: maven
          coordinates: <coordinates of artifact>
  </property>
</extension>

Also you need to add org.arquillian.algeron:arquillian-algeron-provider-maven-retriever:${version} dependency.

Possible attributes:

Parameter Description

coordinates

Mandatory Maven coordinates of contracts. G:A:V format.

offline

Optional flag that sets Maven to work offline.

customSettings

Optional field that sets the location of custom settings.xml file.

remoteRepository

Optional field that sets URL of remote Maven repository

Notice that all these attributes can be set using system properties or environment variable as any other property in arquillian.xml.

Pact Broker Retriever

You can retrieve contracts from Pact Broker artifacts.

arquillian.xml
<extension qualifier="algeron-provider">
  <property name="retrieverConfiguration">
          provider: pactbroker
          url: <url of pact broker server>
          username: <username of pact borker>
          password: <password of pact broker>
          tags: <tag or list of tags
  </property>
</extension>

Also you need to add org.arquillian.algeron:arquillian-algeron-pact-provider-pact-broker-loader:${version} dependency. This is only supported in case of using Pact provider.

Possible attributes:

Parameter Description

url

Mandatory field that sets url of Pact Broker.

username

Optional field that sets the username of Pact Broker.

password

Optional field that sets the password of Pact Broker.

tags

Optional field that sets the tags to retrieve from Pact Broker or latest by default.

Notice that all these attributes can be set using system properties or environment variable as any other property in arquillian.xml.

5.1.3. JBoss Forge Arquillian Addon

Forge Arquillian Addon offers an integration with Arquillian Algeron Retrievers.

To use it apart from having the Forge Arquillian Addon installed in Forge, you also need to have Arquillian Algeron Provider dependencies registered on build tool (for example using command arquillian-algeron-setup-provider --contracts-library pact JBoss Forge Arquillian Provider Addon ).

Each of the publishers have its own command to be registered where you can set its specific configuration parameters.

folder

arquillian-algeron-setup-retriever --retriever folder --contract-folder /tmp

url

arquillian-algeron-setup-retriever --retriever url --url http://localhost

git

arquillian-algeron-setup-retriever --retriever git --url http://localhost:3000

maven

arquillian-algeron-setup-retriever --retriever maven --maven-coordinates org.superbiz:foo:1.0.0

pactbroker

arquillian-algeron-setup-retriever --retriever pactbroker --host http://localhost --port 8081

See it alive in next terminal cast:

104390

5.2. Arquillian Algeron Pact Provider

The next thing you need to do is sent the contract (aka "pact" file) to Provider project and validate that provider produces the expected responses to defined requests. This is done by replaying all requests defined in contract against real provider and validating that the response is the expected one.

5.2.1. Pact Provider In Container

Again you can write provider’s part using in container or standalone mode. In this section, an in container mode example is developed.

Apart from adding Arquillian bom dependencies as mentioned in consumer part, you need to add arquillian algeron pact dependencies:

pom.xml
<dependency>
    <groupId>org.arquillian.algeron</groupId>
    <artifactId>arquillian-algeron-pact-provider-core</artifactId>
    <scope>test</scope>
    <version>${version.arquillian_algeron}</version>
</dependency>
<dependency>
    <groupId>au.com.dius</groupId>
    <artifactId>pact-jvm-provider_2.11</artifactId>
    <scope>test</scope>
    <version>3.5.0-beta.2</version> (1)
</dependency>
1 Arquillian Algeron Pact has been tested with latest version, but it should work with any 3.X version.

After dependencies you can write the test that replies all contracts against provider:

MyServiceProviderTest.java
@RunWith(Arquillian.class)
@Provider("test_provider") (1)
@PactFolder("pacts") (2)
public class MyServiceProviderTest {

    @Deployment(testable = false) (3)
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class).addClass(MyService.class);
    }

    @ArquillianResource (4)
    URL webapp;

    @ArquillianResource (5)
    Target target;

    @Test
    public void should_provide_valid_answers() {
        target.testInteraction(webapp); (6)
    }

}
1 Sets the name of provider used in this test
2 Configures where to get pact files
3 Deploys provider application to app server
4 Arquillian injection of URL where application is deployed
5 A target is a class that makes all requests to provider. In case of Arquillian Algeron Pact by default it uses an Http Client target
6 Makes the request to provider and validates that the response is the expected one

When running this test Arquillian Algeron Pact Provider will do next things:

  1. Deploy provider to chosen application server, as any other Arquillian test.

  2. Read all contract files present in classpath folder pacts which the provider is test_provider. Of course there can be more than one since a provider might serve several consumers.

  3. For each contract, it will extract each of the request/response pair

  4. For each request/response it sends the request specified in contract to provider and validate the response against expectations defined in contract.

So as you can see the should_provide_valid_answers might be run several times depending on the number of consumers that depends on provider and the number of requests/responses defined. But this is something that it is managed automatically by Arquillian Algeron Pact.

You can see full example at: ftest-incontainer

5.2.2. Pact Provider Standalone

The only difference between previous example is that you only need arquillian-junit-standalone instead of arquilian-junit-container and don’t need to define any container adapter. Obviously no @Deployment method is required:

StarWarsProviderTest
@RunWith(Arquillian.class)
@Provider("planets_provider")
@PactFolder("pacts")
public class StarWarsProviderTest {

    @ClassRule (1)
    public static final ClientDriverRule embeddedService = new ClientDriverRule(8332);

    @ArquillianResource
    Target target;

    @BeforeClass (2)
    public static void recordServerInteractions() {
        embeddedService.addExpectation(
                onRequestTo("/rest/planet/orbital/average")
                    .withMethod(ClientDriverRequest.Method.GET),
                giveResponse("1298.3", "text/plain").withStatus(200));

        embeddedService.addExpectation(
                onRequestTo("/rest/planet/orbital/biggest")
                        .withMethod(ClientDriverRequest.Method.GET),
                giveResponseAsBytes(StarWarsProviderTest.class.getResourceAsStream("/server.json"), "application/json").withStatus(200));

    }


    @Test
    public void validateProvider() {
        target.testInteraction();
    }

}
1 Stub server to not having to having to deploy an application
2 Expectations/Implementation of provider server

You can see full example at: ftest

5.2.3. Pact Provider Enrichers

You can enrich your test with current consumer and current request/response interaction by using @CurrentConsumer and CurrentInteraction annotations.

@CurrentConsumer
Consumer consumer;

@CurrentInteraction
RequestResponseInteraction interaction;

5.2.4. Pact Provider Configuration

Arquillian Algeron Pact Provider can be configured using Arquillian configuration mechanism (arquillian.xml file or system properties).

arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://jboss.org/schema/arquillian"
            xsi:schemaLocation="http://jboss.org/schema/arquillian
    http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <extension qualifier="pact-provider">
        <property name="port">8332</property>
    </extension>

</arquillian>

The attributes are:

Attribute Name Description Default Value

targetUrl

Url to used by target to connect to provider

insecure

In case of https if test should skip https validations

false

protocol

protocol used to connect in case of not using targetUrl property

http

host

host used to connect in case of not using targetUrl property

localhost

port

port used to connect in case of not using targetUrl property

8080

path

path appended at the end of the host in case of not using targetUrl property

/

Notice that in case of using incontainer tests you don’t need to configure any of these parameters (except if insecure is required) since you can use the URL injected by Arquillian. See this at provider in container example.

5.2.5. Pact States

Each interaction in a pact should be verified in isolation, with no context maintained from the previous interactions. Provider states allow you to set up data on the provider by injecting it straight into the datasource before the interaction is run, so that it can make a response that matches what the consumer expects.

Provider states also allow the consumer to make the same request with different expected responses.

Provider state is all about the state of the provider, not about the state of the consumer, or about what is in the request.

In consumer side you set an state using given section.

For example:

builder.given("test state")…​ sets state as plain String.

Or in case you want to set some parameters you can do:

Map parameters = new HashMap<>();
builder.given("test state", parameters)...

which in this case sets an state name with some key/value pairs.

But in Arquillian Algeron Pact Provider we also give support for states in the form of Cucumber expression. You can define an state with the form:

builder.given("I have 36 cukes in my belly")…​

So the next question is how can I respond to states in provider side? Let’s see an example in each case:

In first example you only set a state with a name, without any parameter, so in your provider side you are going to do:

@State("test state")
public void testStateMethod(Map<String, Object> params) {
    // Do some data preparation
}

This method is executed if and only if given interaction has defined the state test state.

Obviously in previous example you have no way to pass parameters from consumer to provider. In second example you are passing a map with some parameters. To recover them you need to add as method parameter a Map.

@State("test state")
public void testStateMethod(Map<String, Object> params) {
}

This method is executed if and only if given interaction has defined the state test state and injects the defined parameters in consumer as method parameter.

Also the third case uses Cucumber-lik expression, so you need similar way to take parameters.

@State("I have (\\d+) cukes in my belly")
public void stateMethod(int numberOfCukes) {
    this.numberOfCukes = numberOfCukes;
}

Notice that now as happens with Cucumber you need to specify the regular expression and you get the values as method parameters. Native values and list are supported. Lists are set in consumer side as comma-separated values and scanned as \\s+.

5.2.6. AssertJ integration

Arquillian Algeron Pact Provider implements custom matcher of AssertJ for validating contracts against provider.

First thing to do to use AssertJ matcher is add arquillian-algeron-pact-provider-assertj dependency, and the AssertJ you want to use.

pom.xml
<dependency>
    <groupId>org.arquillian.algeron</groupId>
    <artifactId>arquillian-algeron-pact-provider-assertj</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <scope>test</scope>
</dependency>

Finally in your test you can use the overloaded assertThat static method receiving a Target type.

@ArquillianResource
URL webapp;

@ArquillianResource
Target target;

@Test
public void should_provide_valid_answers() {
    assertThat(target).withUrl(webapp).satisfiesContract();
}

Notice that now instead of writing target.testInteraction(webapp); you are using a more readable way of asserting test.

5.2.7. Arquillian Recorder Reporter integration

Currently pact generates report from contract tests in several formats, including Markdown or JSON. You can also report using Arquillian Recorder Reporter (https://github.com/arquillian/arquillian-recorder) extension.

To use it you need to annotate at test class level with VerificationReports and set type to recorder and add Arquillian Recorder Reporter dependency org.arquillian.extension:arquillian-recorder-reporter-impl:<version>.

you can set more than reporter at once so you can do @VerificationReports(value = {"console", "recorder"}).

5.2.8. JBoss Forge Arquillian Addon

Forge Arquillian Addon offers an integration with Arquillian Algeron Provider.

To use it apart from having the Forge Arquillian Addon installed in Forge, you need to have arquillian dependencies registered on build tool.

To register Arquillian Provider dependencies in build tool just run next command:

arquillian-algeron-setup-provider --contracts-library pact

After that you can enrich a given Arquillian test with provider annotations:

arquillian-algeron-create-provider-test --provider myprovider --test-class org.superbiz.MyContractTest

It is important to note that test class should be already created (for example using arquillian-create-test --named MyContractTest --target-package org.superbiz command).

See it alive in next terminal cast:

104387

5.3. Skipping Deployment

In case of consumer driven contracts, there are two kinds of tests - consumer tests and provider tests. Usually in your CI environment you want to run provider tests against two different scenarios:

against a master branch of provider

to detect if provider team has already implemented all the functionalities defined in contracts.

against (pre)production

if you support deploying consumer independently of a provider, then you need to ensure that if you deploy new consumer with the new contracts to (pre)production everything will be still working and you haven’t introduced any regressions.

So before deploying a new consumer you need to verify that from provider side everything will continue working.

In both cases, the test itself is exactly the same. There is only one slight difference in both cases which how you set up your test environments. In the first scenario, you want to deploy the latest provider code. One way of doing it is using Arquillian container control and @Deployment method to create the package, start the container and deploy it. But in the latter case, when you want to run contract test against provider that is already deployed on (pre)production environment, you don’t need to deploy anything nor control the lifecycle of any container. For this reason we provided skipDeployment flag.

arquillian.xml
<extension qualifier="algeron-provider">
    <property name="skipDeployment">${env.skipDeployment:false}</property>
</extension>

skipDeployment default value by default is set to false, which means that the test will behave as it usually does, but when it is set to @`true, Arquillian is going to ignore anything related to container lifecycle. ` To use this strategy your test needs to be defined as `@RunAsClient. You can think of it as a dynamic way of converting an Arquillian container test into Arquillian standalone test.

Let’s see an example:

MyServiceProviderTest.java
@RunWith(Arquillian.class)
@Provider("test_provider")
@PactFolder("pacts")
@RunAsClient
public class MyServiceProviderTest {

    @Deployment(testable = false)
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class).addClass(MyService.class);
    }

    @ArquillianResource
    @Environment("myservice.url") (1)
    URL webapp;

    @ArquillianResource
    Target target;

    @Test
    public void should_provide_valid_answers() {
        target.testInteraction(webapp);
    }

}
1 Environment annotation to set URL in case of skipping deployment

Given previous test, if skipDeployment is false, this test will behave like:

  1. Start chosen application server (Wildfly, TomEE, Tomcat, …​).

  2. Package and Deploy MyService.

  3. Enrich webapp URL with the one provided by application server. @Environment variable is ignored.

  4. Executes contract test against deployed application.

  5. Undeploy and stop everything.

But if you set skipDeployment to true, lifecycle is slightly different:

  1. Enrich webapp URL with Java system property or environment variable named myservice.url set in @Environment.

  2. Executes contract tests against URL provided by @Environment.

There is no additional "behind the scenes" Arquillian magic involved. Notice that with a simple attribute you can enable/disable how Arquillian behaves regarding the deployment lifecycle, and how you can reuse same test (DRY) for different scenarios.

If you want to enable skipDeployment feature, you can set Java system property or environment variable arq.extension.algeron-provider.skipDeployment to true or by using ${env.skipDeployment:false} form.