<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- This module was also published with a richer model, Gradle metadata,  -->
  <!-- which should be used instead. Do not delete the following line which  -->
  <!-- is to indicate to Gradle or any Gradle module metadata file consumer  -->
  <!-- that they should prefer consuming it instead. -->
  <!-- do_not_remove: published-with-gradle-metadata -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>au.com.dius.pact.provider</groupId>
  <artifactId>spring</artifactId>
  <version>4.2.12</version>
  <name>spring</name>
  <description># Pact Spring/JUnit runner

## Overview
Library provides ability to play contract tests against a provider using Spring &amp;amp; JUnit.
This library is based on and references the JUnit package, so see the [Pact JUnit 4](/provider/junit/README.md) or [Pact JUnit 5](/provider/junit5/README.md) providers for more details regarding configuration using JUnit.

Supports:

- Standard ways to load pacts from folders and broker

- Easy way to change assertion strategy

- Spring Test MockMVC Controllers and ControllerAdvice using MockMvc standalone setup.

- MockMvc debugger output

- Spring WebFlux Controllers and RouterFunctions

- Multiple @State runs to test a particular Provider State multiple times

- **au.com.dius.pact.provider.junit.State** custom annotation - before each interaction that requires a state change,
all methods annotated by `@State` with appropriate the state listed will be invoked.

**NOTE:** For publishing provider verification results to a pact broker, make sure the Java system property `pact.provider.version`
is set with the version of your provider. 

## Example of MockMvc test

```java
    @RunWith(RestPactRunner.class) // Custom pact runner, child of PactRunner which runs only REST tests
    @Provider(&amp;quot;myAwesomeService&amp;quot;) // Set up name of tested provider
    @PactFolder(&amp;quot;pacts&amp;quot;) // Point where to find pacts (See also section Pacts source in documentation)
    public class ContractTest {
        //Create an instance of your controller.  We cannot autowire this as we&amp;apos;re not using (and don&amp;apos;t want to use)  a Spring test runner.
        @InjectMocks
        private AwesomeController awesomeController = new AwesomeController();

        //Mock your service logic class.  We&amp;apos;ll use this to create scenarios for respective provider states.
        @Mock
        private AwesomeBusinessLogic awesomeBusinessLogic;

        //Create an instance of your controller advice (if you have one).  This will be passed to the MockMvcTarget constructor to be wired up with MockMvc.
        @InjectMocks
        private AwesomeControllerAdvice awesomeControllerAdvice = new AwesomeControllerAdvice();

        //Create a new instance of the MockMvcTarget and annotate it as the TestTarget for PactRunner
        @TestTarget
        public final MockMvcTarget target = new MockMvcTarget();

        @Before //Method will be run before each test of interaction
        public void before() {
            //initialize your mocks using your mocking framework
            MockitoAnnotations.initMocks(this);

            //configure the MockMvcTarget with your controller and controller advice
            target.setControllers(awesomeController);
            target.setControllerAdvice(awesomeControllerAdvice);
        }

        @State(&amp;quot;default&amp;quot;, &amp;quot;no-data&amp;quot;) // Method will be run before testing interactions that require &amp;quot;default&amp;quot; or &amp;quot;no-data&amp;quot; state
        public void toDefaultState() {
            target.setRunTimes(3);  //let&amp;apos;s loop through this state a few times for a 3 data variants
            when(awesomeBusinessLogic.getById(any(UUID.class)))
                .thenReturn(myTestHelper.generateRandomReturnData(UUID.randomUUID(), ExampleEnum.ONE))
                .thenReturn(myTestHelper.generateRandomReturnData(UUID.randomUUID(), ExampleEnum.TWO))
                .thenReturn(myTestHelper.generateRandomReturnData(UUID.randomUUID(), ExampleEnum.THREE));
        }

        @State(&amp;quot;error-case&amp;quot;)
        public void SingleUploadExistsState_Success() {
            target.setRunTimes(1); //tell the runner to only loop one time for this state
            
            //you might want to throw exceptions to be picked off by your controller advice
            when(awesomeBusinessLogic.getById(any(UUID.class)))
                .then(i -&amp;gt; { throw new NotCoolException(i.getArgumentAt(0, UUID.class).toString()); });
        }
    }
```

## Example of Spring WebFlux test

```java
    @RunWith(RestPactRunner.class) // Custom pact runner, child of PactRunner which runs only REST tests
    @Provider(&amp;quot;myAwesomeService&amp;quot;) // Set up name of tested provider
    @PactFolder(&amp;quot;pacts&amp;quot;) // Point where to find pacts (See also section Pacts source in documentation)
    public class AwesomeRouterContractTest {

        //Create a new instance of the WebFluxTarget and annotate it as the TestTarget for PactRunner
        @TestTarget
        public WebFluxTarget target = new WebFluxTarget();

        //Create instance of your RouterFunction
        public RouterFunction&amp;lt;ServerResponse&amp;gt; routerFunction
              = new AwesomeRouter(new AwesomeHandler()).routes();

        //Configure the WebFluxTarget with routerFunction
        @Before
        public void setup() {
            target.setRouterFunction(routerFunction);
        }

    }
```

## Using Spring runners

You can use `SpringRestPactRunner` or `SpringMessagePactRunner` instead of the default Pact runner to use the Spring test annotations. This will
allow you to inject or mock spring beans. `SpringRestPactRunner` is for restful webapps and `SpringMessagePactRunner` is
for async message tests.

For example:

```java
@RunWith(SpringRestPactRunner.class)
@Provider(&amp;quot;pricing&amp;quot;)
@PactBroker(protocol = &amp;quot;https&amp;quot;, host = &amp;quot;${pactBrokerHost}&amp;quot;, port = &amp;quot;443&amp;quot;,
authentication = @PactBrokerAuth(username = &amp;quot;${pactBrokerUser}&amp;quot;, password = &amp;quot;${pactBrokerPassword}&amp;quot;))
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class PricingServiceProviderPactTest {

  @MockBean
  private ProductClient productClient;  // This will replace the bean with a mock in the application context

  @TestTarget
  @SuppressWarnings(value = &amp;quot;VisibilityModifier&amp;quot;)
  public final Target target = new HttpTarget(8091);

  @State(&amp;quot;Product X010000021 exists&amp;quot;)
  public void setupProductX010000021() throws IOException {
    reset(productClient);
    ProductBuilder product = new ProductBuilder()
      .withProductCode(&amp;quot;X010000021&amp;quot;);
    when(productClient.fetch((Set&amp;lt;String&amp;gt;) argThat(contains(&amp;quot;X010000021&amp;quot;)), any())).thenReturn(product);
  }

  @State(&amp;quot;the product code X00001 can be priced&amp;quot;)
  public void theProductCodeX00001CanBePriced() throws IOException {
    reset(productClient);
    ProductBuilder product = new ProductBuilder()
      .withProductCode(&amp;quot;X00001&amp;quot;);
    when(productClient.find((Set&amp;lt;String&amp;gt;) argThat(contains(&amp;quot;X00001&amp;quot;)), any())).thenReturn(product);
  }

}
```

### Using Spring Context Properties

The SpringRestPactRunner will look up any annotation expressions (like `${pactBrokerHost}`)
above) from the Spring context. For Springboot, this will allow you to define the properties in the application test properties.

For instance, if you create the following `application.yml` in the test resources:

```yaml
pactbroker:
  host: &amp;quot;your.broker.local&amp;quot;
  port: &amp;quot;443&amp;quot;
  protocol: &amp;quot;https&amp;quot;
  auth:
    username: &amp;quot;&amp;lt;your broker username&amp;gt;&amp;quot;
    password: &amp;quot;&amp;lt;your broker password&amp;gt;&amp;quot;

```

Then you can use the defaults on the `@PactBroker` annotation.

```java
@RunWith(SpringRestPactRunner.class)
@Provider(&amp;quot;My Service&amp;quot;)
@PactBroker(
  authentication =  @PactBrokerAuth(username = &amp;quot;${pactbroker.auth.username}&amp;quot;, password = &amp;quot;${pactbroker.auth.password}&amp;quot;)
)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PactVerificationTest {

```

### Using a random port with a Springboot test
If you use a random port in a springboot test (by setting `SpringBootTest.WebEnvironment.RANDOM_PORT`), you need to set it to the `TestTarget`. How this works is different for JUnit4 and JUnit5.

#### JUnit4
You can use the
`SpringBootHttpTarget` which will get the application port from the spring application context.

For example:

```java
@RunWith(SpringRestPactRunner.class)
@Provider(&amp;quot;My Service&amp;quot;)
@PactBroker
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PactVerificationTest {

  @TestTarget
  public final Target target = new SpringBootHttpTarget();

}
```

#### JUnit5
You actually don&amp;apos;t need to dependend on `pact-jvm-provider-spring` for this. It&amp;apos;s sufficient to depend on `pact-jvm-provider-junit5`. 

You can set the port to the `HttpTestTarget` object in the before method.

```java
@Provider(&amp;quot;My Service&amp;quot;)
@PactBroker
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PactVerificationTest {

    @LocalServerPort
    private int port;

    @BeforeEach
    void before(PactVerificationContext context) {
        context.setTarget(new HttpTestTarget(&amp;quot;localhost&amp;quot;, port));
    }

}
```


</description>
  <url>https://github.com/DiUS/pact-jvm</url>
  <licenses>
    <license>
      <name>Apache 2</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  <developers>
    <developer>
      <id>thetrav</id>
      <name>Travis Dixon</name>
      <email>the.trav@gmail.com</email>
    </developer>
    <developer>
      <id>rholshausen</id>
      <name>Ronald Holshausen</name>
      <email>rholshausen@dius.com.au</email>
    </developer>
  </developers>
  <scm>
    <connection>https://github.com/DiUS/pact-jvm.git</connection>
    <url>https://github.com/DiUS/pact-jvm</url>
  </scm>
  <dependencies>
    <dependency>
      <groupId>au.com.dius.pact.provider</groupId>
      <artifactId>junit</artifactId>
      <version>4.2.12</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-stdlib</artifactId>
      <version>1.4.32</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-reflect</artifactId>
      <version>1.4.32</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <version>2.2.5.RELEASE</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.3.RELEASE</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webflux</artifactId>
      <version>5.2.3.RELEASE</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.3.RELEASE</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-joda</artifactId>
      <version>2.10.2</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.synchronoss.cloud</groupId>
      <artifactId>nio-multipart-parser</artifactId>
      <version>1.1.0</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>
