{"id":38966980,"url":"https://github.com/tyro/pact-spring-mvc","last_synced_at":"2026-01-17T16:31:39.148Z","repository":{"id":19215726,"uuid":"85902173","full_name":"tyro/pact-spring-mvc","owner":"tyro","description":"A library to enable recording/publishing and download/playback of PACT test files.","archived":false,"fork":false,"pushed_at":"2024-10-03T18:43:16.000Z","size":206,"stargazers_count":6,"open_issues_count":2,"forks_count":5,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-12-09T18:40:36.457Z","etag":null,"topics":["pact","spring","testing"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tyro.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-23T03:19:36.000Z","updated_at":"2022-08-05T03:32:02.000Z","dependencies_parsed_at":"2022-08-07T09:01:12.920Z","dependency_job_id":null,"html_url":"https://github.com/tyro/pact-spring-mvc","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/tyro/pact-spring-mvc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tyro%2Fpact-spring-mvc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tyro%2Fpact-spring-mvc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tyro%2Fpact-spring-mvc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tyro%2Fpact-spring-mvc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tyro","download_url":"https://codeload.github.com/tyro/pact-spring-mvc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tyro%2Fpact-spring-mvc/sbom","scorecard":{"id":905624,"data":{"date":"2025-08-11","repo":{"name":"github.com/tyro/pact-spring-mvc","commit":"15a2301bddc6acfb898eea3e720986e774b0a0cd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.1,"checks":[{"name":"Code-Review","score":1,"reason":"Found 3/19 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"31 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-h46c-h94j-95f3","Warn: Project is vulnerable to: GHSA-wf8f-6423-gfxg","Warn: Project is vulnerable to: GHSA-jjjh-jjxp-wpff","Warn: Project is vulnerable to: GHSA-rgv9-q543-rqg4","Warn: Project is vulnerable to: GHSA-78wr-2p64-hpwj","Warn: Project is vulnerable to: GHSA-j288-q9x7-2f5v","Warn: Project is vulnerable to: GHSA-3vqj-43w4-2q58","Warn: Project is vulnerable to: GHSA-4jq9-2xhw-jpx7","Warn: Project is vulnerable to: GHSA-f3jh-qvm4-mg39","Warn: Project is vulnerable to: GHSA-hh32-7344-cg2f","Warn: Project is vulnerable to: GHSA-q3v6-hm2v-pw99","Warn: Project is vulnerable to: GHSA-w9jg-gvgr-354m","Warn: Project is vulnerable to: GHSA-wx54-3278-m5g4","Warn: Project is vulnerable to: GHSA-c4q5-6c82-3qpw","Warn: Project is vulnerable to: GHSA-36p3-wjmg-h94x","Warn: Project is vulnerable to: GHSA-hh26-6xwr-ggv7","Warn: Project is vulnerable to: GHSA-4gc7-5j7h-4qph","Warn: Project is vulnerable to: GHSA-4wp7-92pw-q264","Warn: Project is vulnerable to: GHSA-g5mm-vmx4-3rg7","Warn: Project is vulnerable to: GHSA-6gf2-pvqw-37ph","Warn: Project is vulnerable to: GHSA-rfmp-97jj-h8m6","Warn: Project is vulnerable to: GHSA-558x-2xjg-6232","Warn: Project is vulnerable to: GHSA-564r-hj7v-mcr5","Warn: Project is vulnerable to: GHSA-9cmq-m9j5-mvww","Warn: Project is vulnerable to: GHSA-wxqc-pxw9-g2p8","Warn: Project is vulnerable to: GHSA-2rmj-mq67-h97g","Warn: Project is vulnerable to: GHSA-2wrp-6fg6-hmc5","Warn: Project is vulnerable to: GHSA-4wrc-f8pq-fpqp","Warn: Project is vulnerable to: GHSA-ccgv-vj62-xf9h","Warn: Project is vulnerable to: GHSA-gfwj-fwqj-fp3v","Warn: Project is vulnerable to: GHSA-hgjh-9rj2-g67j"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T17:14:37.145Z","repository_id":19215726,"created_at":"2025-08-24T17:14:37.145Z","updated_at":"2025-08-24T17:14:37.145Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28511867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T13:38:16.342Z","status":"ssl_error","status_checked_at":"2026-01-17T13:37:44.060Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pact","spring","testing"],"created_at":"2026-01-17T16:31:39.061Z","updated_at":"2026-01-17T16:31:39.137Z","avatar_url":"https://github.com/tyro.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pact-spring-mvc\n\n[![Download](https://maven-badges.herokuapp.com/maven-central/com.tyro.oss.pact/pact-spring-mvc/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.tyro.oss.pact/pact-spring-mvc)\n[![Build Status](https://travis-ci.org/tyro/pact-spring-mvc.svg?branch=master)](https://travis-ci.org/tyro/pact-spring-mvc)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)\n\nA library to enable recording/publishing and download/playback of PACT test files.\n\nThis library is not thread safe - making mock mvc calls from multiple threads will not work. This reflects the underlying behaviour of the spring test framework.\n\n## Purpose\n\nThis project allows you to record your integration tests (using a mocked server) against a client and then replay them against the real server to make sure your\nmocks are still faking the server correctly and that your server and client are playing nicely.\n\n## Getting Started\n\n`pact-spring-mvc` is available on Maven Central.\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.tyro.oss.pact\u003c/groupId\u003e\n    \u003cartifactId\u003epact-spring-mvc\u003c/artifactId\u003e\n    \u003cversion\u003e2.0.1\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n`pact-spring-mvc` requires URLs for a [PACT broker](https://github.com/bethesque/pact_broker) in order to publish and download PACT files.\nYou will need to specify the URLs when using this library. This can be done by:\n    1. Specifying System Properties with maven or on the command line. (higher precedence)\n    2. A properties file named pact-broker.properties that needs to exist on the test classpath.\n\nThe required properties are:\n\n*  `pact.broker.publish.url` - used to publish the pact on behalf of the consumer\n*  `pact.broker.download.url` - used to download the pact on behalf of the provider\n\nIf you don't specify these properties an exception will be thrown.\n\n## Maven Configuration to publish PACTs\n    \u003cprofiles\u003e\n        \u003cprofile\u003e\n            \u003cid\u003epublish-pact\u003c/id\u003e\n            \u003cactivation\u003e\n                \u003cactiveByDefault\u003efalse\u003c/activeByDefault\u003e\n            \u003c/activation\u003e\n            \u003cbuild\u003e\n                \u003cplugins\u003e\n                    \u003cplugin\u003e\n                        \u003cgroupId\u003eorg.codehaus.mojo\u003c/groupId\u003e\n                        \u003cartifactId\u003eexec-maven-plugin\u003c/artifactId\u003e\n                        \u003cexecutions\u003e\n                            \u003cexecution\u003e\n                                \u003cid\u003epublish-all-pacts\u003c/id\u003e\n                                \u003cphase\u003einstall\u003c/phase\u003e\n                                \u003cgoals\u003e\n                                    \u003cgoal\u003ejava\u003c/goal\u003e\n                                \u003c/goals\u003e\n                                \u003cconfiguration\u003e\n                                    \u003cmainClass\u003ecom.tyro.oss.pact.spring.pact.consumer.PactPublisher\u003c/mainClass\u003e\n                                    \u003cclasspathScope\u003etest\u003c/classpathScope\u003e\n                                    \u003ccommandlineArgs\u003e{{your-artifactName Here}} ${project.version} ${project.basedir}/target/pact/\u003c/commandlineArgs\u003e\n                                \u003c/configuration\u003e\n                            \u003c/execution\u003e\n                        \u003c/executions\u003e\n                    \u003c/plugin\u003e\n                \u003c/plugins\u003e\n            \u003c/build\u003e\n        \u003c/profile\u003e\n    \u003c/profiles\u003e\n\n## Trial by Example\n\nThe following sections describe different use case examples.  These examples can be found in the test tree of the project in all their glory.\n\n### Simple Contract\n\nThe most basic example of a contract test is the retrieval of a object.  In this case, we will be retrieving a DTO containing a single integer.  The request\ncontains no request body or parameters.  We will be wrapping this call in an IntegerService, shown below.\n\n~~~\npublic class IntegerService {\n\n    private RestRequestInvoker restRequestInvoker;\n\n    public IntegerService(RestTemplate restTemplate) {\n        this.restRequestInvoker = new RestRequestInvoker(restTemplate);\n    }\n\n    public IntegerDTO getLatestInteger() {\n        return restRequestInvoker.forDescriptor(new RestRequestDescriptor\u003c\u003e(\"/integer\", HttpMethod.GET, null, IntegerDTO.class));\n    }\n}\n~~~\n\nThe service itself is quite simple, it creates a RestRequestDescriptor and passes it to our invoker to make the call.  The details of both are left as an\nexercise for the reader :)\n\nNext we will define our contract expectations. We create the class SimpleConsumerPactTest.\n\n~~~\nclass SimpleConsumerPactTest {\n\n    ...\n    private TuPactRecordingServer recordingServer;\n    private IntegerService integerService;\n\n    @BeforeEach\n    void setup() throws Exception {\n        RestTemplate restTemplate = new RestTemplate();\n\n        integerService = new IntegerService(restTemplate);\n\n        File pactFile = new File(\"target/pact/example_provider_pacts.json\");\n        recordingServer = TuPactRecordingServer.createServer(restTemplate, pactFile);\n    }\n\n    ...\n\n    @AfterEach\n    void closeTuPact() throws Exception {\n        recordingServer.close();\n    }\n}\n~~~\n\nIn our setup, create a new TuPactRecordingServer with a RestTemplate and the File to which we will write our contract.  If the file\nalready exists, we will append our contracts to it, so it is helpful to place it in the target directory to allow `mvn clean` to clean\nit up for us.  We also initialise our service under test with the same RestTemplate.\n\nWe also need to remember to close our RecordingServer in our teardown to verify our expectations were fulfilled, ensure our contract\nfile is flushed to disk and closed, and return the RestTemplate to its original state.\n\n~~~\nclass SimpleConsumerPactTest {\n\n    @BeforeEach\n    ...\n\n    @Test\n    void shouldRetrieveDefaultValue(TestInfo testInfo) {\n        recordingServer.startWorkflow(testInfo.getDisplayName());\n\n        recordingServer.expect(new RestRequestDescriptor\u003c\u003e(\"/integer\", HttpMethod.GET, null, IntegerDTO.class))\n                .andReturn(new IntegerDTO(0));\n\n        assertThat(integerService.getLatestInteger(), is(new IntegerDTO(0)));\n    }\n\n    @AfterEach\n    ...\n}\n~~~\n\nThe test itself needs to initialise a new TuPact workflow, giving it an name that can be used to trace back to the contract definition\nif it happens to fail on the provider side.  Using the TestName rule has proven to be most useful when you have access to both the consumer\nand provider.\n\nOnce the workflow has been named, we can start defining expected behaviour.  In our case, we expect the same RestRequestDescriptor and return\nan IntegerDTO with a value of 0.  We are then set up and can test our services behaviour.\n\nRunning that test, we can see the file we declared in the setup is created in the target directory.  We will use this file directly, instead of\nusing a Pact Broker, to build our provider test.\n\nOur provider controller for this example is even simpler than our service.\n\n~~~\n@RestController\npublic class IntegerController {\n\n    @RequestMapping(value = \"/integer\")\n    public IntegerDTO getLatestInteger() {\n        return new IntegerDTO(0);\n    }\n}\n~~~\n\nAs you can see, we have hard-coded the IntegerDTO value, so there is no state to confuse our tests.\n\n~~~\n@PactDefinition(\n        provider = \"example-provider\",\n        consumer = \"example-consumer\",\n        localPactFilePath = \"target/pact/example_provider_pacts.json\"\n)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SimpleWebConfig.class)\n@WebAppConfiguration\npublic class SimpleProviderPactTest extends PactTest {\n\n    @Override\n    protected String getServletContextPathWithoutTrailingSlash() {\n        return \"/servletContextPath\";\n    }\n}\n~~~\n\nThe pact test is created by extending PactTest and defining the pacts to execute using the PactDefinition annotation.  The `provider` and\n`consumer` fields are required and must align with those used by the consumer when publishing the pact file to the Broker.  In our case, however,\nwe are overriding that behaviour using the `localPactFilePath` field to indicate the same file we generated above.\n\n    Note: the use of localPactFilePath in our\n\n    examples creates a dependency between our two tests.\n    The consumer test must be run before the provider test.\n\nWe have also added annotations to define our configuration and declare us a web app.  Finally, we have overridden the ```getServletContextPathWithoutTrailingSlash```.\n\nAssuming our ```WebConfig``` and controller are properly configured (they are), this is sufficient to find the pact file,\nload it and execute it in the WebContext.\n\n### Stateful Contract\n\nThe simple example is just that, simple.  It assumes you have a provider with no internal state that can affect the results of your request. So how do you\ndeal with a state-dependent contract?\n\nOur next example enforces the contract of a bookshelf application.  On the consumer side, we have a simple service that wraps two calls to the provider, to\nget a listing of books you own and to get a listing of books you've read.\n\n~~~\npublic class BookshelfService {\n\n    private RestRequestInvoker restRequestInvoker;\n\n    public BookshelfService(RestTemplate restTemplate) {\n        this.restRequestInvoker = new RestRequestInvoker(restTemplate);\n    }\n\n    public BookCollectionDTO getBooksOwned() {\n        return restRequestInvoker.forDescriptor(new RestRequestDescriptor\u003c\u003e(\"/shelf\", HttpMethod.GET, null, BookCollectionDTO.class));\n    }\n\n    public BookCollectionDTO getBooksRead() {\n        return restRequestInvoker.forDescriptor(new RestRequestDescriptor\u003c\u003e(\"/read\", HttpMethod.GET, null, BookCollectionDTO.class));\n    }\n}\n~~~\n\nThe provider can only meet the contract the consumer will define if it has the same assumptions about its default state. One option is to simply agree on a\ndefault state, but that creates a bit of a maintenance nightmare.  We have taken the approach of separating the responsibility of defining the state between\nthe provider and the consumer. The provider is responsible for defining methods to build the expected state, and the consumer is responsible for providing the\nrequired initialisation data for those methods.\n\nThe setup of this consumer test, below, is similar to the previous one, but it adds calls to the ```inState``` method.  This method records the name of the provider\nstate and zero or more objects that will be passed into it. These objects must conform to the method signature provided by the provider.\n\n~~~\nclass StatefulConsumerPactTest {\n\n    ...\n\n    @BeforeEach\n    void setup() throws Exception {\n        ...\n\n        shelvedRead = new BookDTO(\"Essentialism: The Disciplined Pursuite of Less\", true, true);\n        shelvedUnread = new BookDTO(\"Quiet: The Power of Introverts in a World That Can't Stop Talking\", true, false);\n        unshelvedRead = new BookDTO(\"Harry Potter and the Order of the Phoenix\", false, true);\n        unshelvedUnread = new BookDTO(\"The Winds of Winter\", false, false);\n\n        recordingServer = TuPactRecordingServer.createServer(restTemplate, pactFile)\n                .inState(\"withKnownBook\", shelvedRead)\n                .inState(\"withKnownBook\", shelvedUnread)\n                .inState(\"withKnownBook\", unshelvedRead)\n                .inState(\"withKnownBook\", unshelvedUnread);\n    }\n\n    @Test\n    ...\n}\n~~~\n\nThe test methods below proceed in the same manner as the previous example, except that now our expectations can reflect the state we've defined above.\n\n~~~\nclass StatefulConsumerPactTest {\n\n    @BeforeEach\n    ...\n\n    @Test\n    void shouldRetrieveEntireBookshelf(TestInfo testInfo) {\n        recordingServer.startWorkflow(testInfo.getDisplayName());\n\n        recordingServer\n                .expect(getBooksOwnedDescriptor())\n                .andReturn(new BookCollectionDTO(asList(shelvedRead, shelvedUnread)));\n\n        assertThat(bookshelfService.getBooksOwned(), is(new BookCollectionDTO(shelvedRead, shelvedUnread)));\n    }\n\n    @Test\n    void shouldRetrieveEntireReadList(TestInfo testInfo) {\n        recordingServer.startWorkflow(testInfo.getDisplayName());\n\n        recordingServer\n                .expect(getBooksReadDescriptor())\n                .andReturn(new BookCollectionDTO(asList(shelvedRead, unshelvedRead)));\n\n        assertThat(bookshelfService.getBooksRead(), is(new BookCollectionDTO(shelvedRead, unshelvedRead)));\n    }\n\n    @AfterEach\n    ...\n}\n~~~\n\nThe provider test, below, has one additional feature to support the loading of shared state.  The ```@ProviderState``` defines a method that the PactRunner\nwill call to load the objects added to the contract by the ```withState``` call above.\n\n~~~\n@PactTestRunner.PactDefinition(\n        provider = \"example-provider\",\n        consumer = \"example-consumer\",\n        localPactFilePath = \"target/pact/stateful_contract_pacts.json\"\n)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = StatefulWebConfig.class)\n@WebAppConfiguration\nclass StatefulProviderPactTest extends PactTest {\n\n    @Autowired\n    private Bookshelf bookshelf;\n\n    @Override\n    protected String getServletContextPathWithoutTrailingSlash() {\n        return \"/servletContextPath\";\n    }\n\n    @ProviderState\n    public void withKnownBook(BookDTO book) {\n        bookshelf.addBook(book);\n    }\n}\n~~~\n\n## Copyright and Licensing\n\nCopyright (C) 2016 - 2020 Tyro Payments Limited\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n## Contributing\n\nSee [CONTRIBUTING](CONTRIBUTING.md) for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftyro%2Fpact-spring-mvc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftyro%2Fpact-spring-mvc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftyro%2Fpact-spring-mvc/lists"}