This workshop should take about 2 hours, depending on how deep you want to go into each topic.
This workshop is setup with a number of steps that can be run through. Each step is in a branch, so to run through a step of the workshop just check out the branch for that step (i.e. git checkout step1
).
NOTE: Each step is tied to, and must be run within, a git branch, allowing you to progress through each stage incrementally. For example, to move to step 2 run the following: git checkout step2
There are two components in scope for our workshop.
We need to first create an HTTP client to make the calls to our provider service:
The Consumer has implemented the product service client which has the following:
GET /products
- Retrieve all productsGET /products/{id}
- Retrieve a single product by IDThe diagram below highlights the interaction for retrieving a product with ID 10:
You can see the client interface we created in consumer/src/main/au/com/dius/pactworkshop/consumer/ProductService.java
:
@Service public class ProductService { private final RestTemplate restTemplate; @Autowired public ProductService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public List<Product> getAllProducts() { return restTemplate.exchange("/products", HttpMethod.GET, null, new ParameterizedTypeReference<List<Product>>(){}).getBody(); } public Product getProduct(String id) { return restTemplate.getForEntity("/products/{id}", Product.class, id).getBody(); } }
We can run the client with ./gradlew consumer:bootRun
- it should fail with the error below, because the Provider is not running.
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8085/products": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
Move on to step 2
Step 2 - Client Tested but integration failsNow let's create a basic test for our API client. We're going to check 2 things:
You can see the client interface test we created in consumer/src/test/java/au/com/dius/pactworkshop/consumer/ProductServiceTest.java
:
class ProductServiceTest { private WireMockServer wireMockServer; private ProductService productService; @BeforeEach void setUp() { wireMockServer = new WireMockServer(options().dynamicPort()); wireMockServer.start(); RestTemplate restTemplate = new RestTemplateBuilder() .rootUri(wireMockServer.baseUrl()) .build(); productService = new ProductService(restTemplate); } @AfterEach void tearDown() { wireMockServer.stop(); } @Test void getAllProducts() { wireMockServer.stubFor(get(urlPathEqualTo("/products")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("[" + "{\"id\":\"9\",\"type\":\"CREDIT_CARD\",\"name\":\"GEM Visa\",\"version\":\"v2\"},"+ "{\"id\":\"10\",\"type\":\"CREDIT_CARD\",\"name\":\"28 Degrees\",\"version\":\"v1\"}"+ "]"))); List<Product> expected = Arrays.asList(new Product("9", "CREDIT_CARD", "GEM Visa", "v2"), new Product("10", "CREDIT_CARD", "28 Degrees", "v1")); List<Product> products = productService.getAllProducts(); assertEquals(expected, products); } @Test void getProductById() { wireMockServer.stubFor(get(urlPathEqualTo("/products/50")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"id\":\"50\",\"type\":\"CREDIT_CARD\",\"name\":\"28 Degrees\",\"version\":\"v1\"}"))); Product expected = new Product("50", "CREDIT_CARD", "28 Degrees", "v1"); Product product = productService.getProduct("50"); assertEquals(expected, product); } }
Let's run this test and see it all pass:
> ./gradlew consumer:test BUILD SUCCESSFUL in 2s
Meanwhile, our provider team has started building out their API in parallel. Let's run our website against our provider (you'll need two terminals to do this):
# Terminal 1 ❯ ./gradlew provider:bootRun ... ... Tomcat started on port(s): 8085 (http) with context path '' Started ProviderApplication in 1.67 seconds (JVM running for 2.039)
# Terminal 2 > ./gradlew consumer:bootRun --console plain ... ... Started ConsumerApplication in 1.106 seconds (JVM running for 1.62) Products -------- 1) Gem Visa 2) MyFlexiPay 3) 28 Degrees Select item to view details:
You should now see 3 different products. Choosing an index number should display detailed product information.
Let's see what happens!
Doh! We are getting 404 every time we try to view detailed product information. On closer inspection, the provider only knows about /product/{id}
and /products
.
We need to have a conversation about what the endpoint should be, but first...
Move on to step 3
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4