From d215c68534505822f637a328eb7485c246c95041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 12 Apr 2024 12:03:34 +0200 Subject: [PATCH] Migrate to Spring 6.2's AssertJ based MVC assertions. Co-authored-by: Oliver Drotbohm --- .../restbucks/AbstractWebIntegrationTest.java | 78 ++++------- .../web/OrderResourceIntegrationTest.java | 15 +- .../web/PaymentProcessIntegrationTest.java | 130 ++++++++++-------- 3 files changed, 108 insertions(+), 115 deletions(-) diff --git a/server/src/test/java/org/springsource/restbucks/AbstractWebIntegrationTest.java b/server/src/test/java/org/springsource/restbucks/AbstractWebIntegrationTest.java index 6bdafb8e..60278a2b 100644 --- a/server/src/test/java/org/springsource/restbucks/AbstractWebIntegrationTest.java +++ b/server/src/test/java/org/springsource/restbucks/AbstractWebIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 the original author or authors. + * Copyright 2013-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,26 +15,23 @@ */ package org.springsource.restbucks; -import static org.assertj.core.api.Assertions.*; - -import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import java.util.Locale; -import java.util.Optional; +import org.assertj.core.api.Condition; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.client.LinkDiscoverer; import org.springframework.hateoas.client.LinkDiscoverers; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.assertj.MockMvcTester; +import org.springframework.test.web.servlet.assertj.MvcTestResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; /** @@ -48,66 +45,43 @@ public abstract class AbstractWebIntegrationTest { @Autowired WebApplicationContext context; @Autowired LinkDiscoverers links; - protected MockMvc mvc; + protected MockMvcTester mvc; @BeforeEach void setUp() { - mvc = MockMvcBuilders.webAppContextSetup(context).// - defaultRequest(MockMvcRequestBuilders.get("/").locale(Locale.US)).// - build(); + this.mvc = MockMvcTester.from(context, builder ->// + builder.defaultRequest(MockMvcRequestBuilders.get("/").locale(Locale.US)).// + build()); } /** - * Creates a {@link ResultMatcher} that checks for the presence of a link with the given rel. + * Creates a AssertJ {@link Condition} that checks for the presence of a {@link Link} with the given + * {@link LinkRelation}. * - * @param rel - * @return + * @param rel must not be {@literal null}. + * @return will never be {@literal null}. */ - protected ResultMatcher linkWithRelIsPresent(LinkRelation rel) { - return new LinkWithRelMatcher(rel, true); - } + protected Condition linkWithRel(LinkRelation rel) { - /** - * Creates a {@link ResultMatcher} that checks for the non-presence of a link with the given rel. - * - * @param rel - * @return - */ - protected ResultMatcher linkWithRelIsNotPresent(LinkRelation rel) { - return new LinkWithRelMatcher(rel, false); + Assert.notNull(rel, "LinkRelation must not be null!"); + + return new Condition<>(it -> hasLink(it.getMvcResult(), rel), "Expected to find link with relation %s!", rel); } + @SuppressWarnings("null") protected LinkDiscoverer getDiscovererFor(MockHttpServletResponse response) { return links.getRequiredLinkDiscovererFor(response.getContentType()); } - @RequiredArgsConstructor - private class LinkWithRelMatcher implements ResultMatcher { - - private final LinkRelation rel; - private final boolean present; - - /* - * (non-Javadoc) - * @see org.springframework.test.web.servlet.ResultMatcher#match(org.springframework.test.web.servlet.MvcResult) - */ - @Override - public void match(MvcResult result) throws Exception { - - MockHttpServletResponse response = result.getResponse(); - String content = response.getContentAsString(); - LinkDiscoverer discoverer = links.getRequiredLinkDiscovererFor(response.getContentType()); - - Optional link = discoverer.findLinkWithRel(rel, content); - - assertThat(link).matches(it -> it.isPresent() == present, getMessage(link)); - } + @SneakyThrows + @SuppressWarnings("null") + private boolean hasLink(MvcResult result, LinkRelation rel) { - private String getMessage(Optional link) { + var response = result.getResponse(); + var content = response.getContentAsString(); + var discoverer = links.getRequiredLinkDiscovererFor(response.getContentType()); - return String.format("Expected to %s link with relation %s, but found %s!", - present ? "find" : "not find", rel, present ? link.get() : "none"); - } + return discoverer.findLinkWithRel(rel, content).isPresent(); } } diff --git a/server/src/test/java/org/springsource/restbucks/order/web/OrderResourceIntegrationTest.java b/server/src/test/java/org/springsource/restbucks/order/web/OrderResourceIntegrationTest.java index a1f31e6a..bafb35cf 100644 --- a/server/src/test/java/org/springsource/restbucks/order/web/OrderResourceIntegrationTest.java +++ b/server/src/test/java/org/springsource/restbucks/order/web/OrderResourceIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 the original author or authors. + * Copyright 2013-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,12 @@ */ package org.springsource.restbucks.order.web; -import static org.hamcrest.CoreMatchers.*; +import static org.assertj.core.api.Assertions.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.junit.jupiter.api.Test; import org.springframework.hateoas.MediaTypes; +import org.springframework.http.HttpStatus; import org.springsource.restbucks.AbstractWebIntegrationTest; /** @@ -33,9 +33,10 @@ class OrderResourceIntegrationTest extends AbstractWebIntegrationTest { @Test void exposesOrdersResourceViaRootResource() throws Exception { - mvc.perform(get("/")) // - .andExpect(status().isOk()) // - .andExpect(content().contentTypeCompatibleWith(MediaTypes.HAL_JSON)) // - .andExpect(jsonPath("$._links.restbucks:orders.href", notNullValue())); + var result = mvc.perform(get("/")); // + + assertThat(result).hasStatus(HttpStatus.OK); + assertThat(result).contentType().isCompatibleWith(MediaTypes.HAL_JSON); + assertThat(result).bodyJson().hasPath("$._links.restbucks:orders.href").isNotNull(); } } diff --git a/server/src/test/java/org/springsource/restbucks/payment/web/PaymentProcessIntegrationTest.java b/server/src/test/java/org/springsource/restbucks/payment/web/PaymentProcessIntegrationTest.java index 86b43ca7..4d6e209b 100644 --- a/server/src/test/java/org/springsource/restbucks/payment/web/PaymentProcessIntegrationTest.java +++ b/server/src/test/java/org/springsource/restbucks/payment/web/PaymentProcessIntegrationTest.java @@ -16,9 +16,7 @@ package org.springsource.restbucks.payment.web; import static org.assertj.core.api.Assertions.*; -import static org.hamcrest.Matchers.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONArray; @@ -112,6 +110,7 @@ void cancelOrderBeforePayment() throws Exception { var response = accessRootResource(); response = createNewOrder(response); + cancelOrder(response); } @@ -125,11 +124,14 @@ private MockHttpServletResponse accessRootResource() throws Exception { LOG.info("Accessing root resource…"); - return mvc.perform(get("/") // - .accept(MediaTypes.HAL_FORMS_JSON)) // - .andExpect(status().isOk()) // - .andExpect(linkWithRelIsPresent(ORDERS_REL)) // - .andReturn().getResponse(); + var response = mvc.perform(get("/") // + .accept(MediaTypes.HAL_FORMS_JSON)); + + assertThat(response) + .hasStatus(HttpStatus.OK) + .has(linkWithRel(ORDERS_REL)); + + return response.getMvcResult().getResponse(); } /** @@ -141,6 +143,7 @@ private MockHttpServletResponse accessRootResource() throws Exception { * @return * @throws Exception */ + @SuppressWarnings("null") private MockHttpServletResponse createNewOrder(MockHttpServletResponse source) throws Exception { String content = source.getContentAsString(); @@ -151,7 +154,7 @@ private MockHttpServletResponse createNewOrder(MockHttpServletResponse source) t var drinksTemplate = parse.read("$._templates.default.properties[0].options.link.href", String.class); var drinksOptionsUri = Link.of(drinksTemplate).expand().getHref(); var drinksOptionsResponse = mvc.perform(get(drinksOptionsUri)) - .andReturn().getResponse().getContentAsString(); + .getMvcResult().getResponse().getContentAsString(); var drinkUri = JsonPath.parse(drinksOptionsResponse).read("$._embedded.drinks[0].value", String.class); // Select location @@ -161,14 +164,16 @@ private MockHttpServletResponse createNewOrder(MockHttpServletResponse source) t var ordersLink = getDiscovererFor(source).findRequiredLinkWithRel(ORDERS_REL, content); - var result = mvc.perform(post(ordersLink.expand().getHref()) // + var response = mvc.perform(post(ordersLink.expand().getHref()) // .contentType(MediaType.APPLICATION_JSON) - .content(new ObjectMapper().writeValueAsString(payload))) // - .andExpect(status().isCreated()) // - .andExpect(header().string("Location", is(notNullValue()))) // - .andReturn().getResponse(); + .content(new ObjectMapper().writeValueAsString(payload))); + + assertThat(response) + .hasStatus(HttpStatus.CREATED) + .headers().containsHeader(HttpHeaders.LOCATION); - return mvc.perform(get(result.getHeader("Location"))).andReturn().getResponse(); + return mvc.perform(get(response.getMvcResult().getResponse().getHeader(HttpHeaders.LOCATION))) + .getMvcResult().getResponse(); } /** @@ -186,9 +191,11 @@ private MockHttpServletResponse discoverOrdersResource(MockHttpServletResponse s LOG.info("Root resource returned: " + content); LOG.info(String.format("Found orders link pointing to %s… Following…", ordersLink)); - var response = mvc.perform(get(ordersLink.expand().getHref())). // - andExpect(status().isOk()). // - andReturn().getResponse(); + var result = mvc.perform(get(ordersLink.expand().getHref())); + + assertThat(result).hasStatus(HttpStatus.OK); + + var response = result.getMvcResult().getResponse(); LOG.info("Found orders: " + response.getContentAsString()); return response; @@ -222,12 +229,15 @@ private MockHttpServletResponse accessFirstOrder(MockHttpServletResponse source) LOG.info(String.format("Picking first order using JSONPath expression %s…", FIRST_ORDER_EXPRESSION)); LOG.info(String.format("Discovered self link pointing to %s… Following", orderLink)); - return mvc.perform(get(orderLink.getHref())). // - andExpect(linkWithRelIsPresent(IanaLinkRelations.SELF)). // - andExpect(linkWithRelIsPresent(CANCEL_REL)). // - andExpect(linkWithRelIsPresent(UPDATE_REL)). // - andExpect(linkWithRelIsPresent(PAYMENT_REL)).// - andReturn().getResponse(); + var result = mvc.perform(get(orderLink.getHref())); + + assertThat(result) + .has(linkWithRel(IanaLinkRelations.SELF)) + .has(linkWithRel(CANCEL_REL)) + .has(linkWithRel(UPDATE_REL)) + .has(linkWithRel(PAYMENT_REL)); + + return result.getMvcResult().getResponse(); } /** @@ -256,9 +266,9 @@ private MockHttpServletResponse triggerPayment(MockHttpServletResponse response) .contentType(MediaType.APPLICATION_JSON)// .accept(MediaTypes.HAL_JSON)); - var result = action.andExpect(status().isCreated()). // - andExpect(linkWithRelIsPresent(ORDER_REL)). // - andReturn().getResponse(); + assertThat(action) + .hasStatus(HttpStatus.CREATED) + .has(linkWithRel(ORDER_REL)); LOG.info("Payment triggered…"); @@ -266,9 +276,11 @@ private MockHttpServletResponse triggerPayment(MockHttpServletResponse response) LOG.info("Faking a cancel request to make sure it's forbidden…"); var selfLink = discoverer.findRequiredLinkWithRel(IanaLinkRelations.SELF, content); - mvc.perform(delete(selfLink.getHref())).andExpect(status().isMethodNotAllowed()); - return result; + assertThat(mvc.perform(delete(selfLink.getHref()))) + .hasStatus(HttpStatus.METHOD_NOT_ALLOWED); + + return action.getMvcResult().getResponse(); } /** @@ -302,7 +314,7 @@ private MockHttpServletResponse pollUntilOrderHasReceiptLink(MockHttpServletResp LOG.info("Poll state of order until receipt is ready…"); var action = mvc.perform(get(orderLink.expand().getHref()).headers(headers)); - pollResponse = action.andReturn().getResponse(); + pollResponse = action.getMvcResult().getResponse(); var status = pollResponse.getStatus(); etag = pollResponse.getHeader("ETag"); @@ -311,14 +323,15 @@ private MockHttpServletResponse pollUntilOrderHasReceiptLink(MockHttpServletResp if (status == HttpStatus.OK.value()) { - action.andExpect(linkWithRelIsPresent(IanaLinkRelations.SELF)). // - andExpect(linkWithRelIsNotPresent(UPDATE_REL)). // - andExpect(linkWithRelIsNotPresent(CANCEL_REL)); + assertThat(action) + .has(linkWithRel(IanaLinkRelations.SELF)) + .doesNotHave(linkWithRel(UPDATE_REL)) + .doesNotHave(linkWithRel(CANCEL_REL)); receiptLink = discoverer.findLinkWithRel(RECEIPT_REL, pollResponse.getContentAsString()); } else if (status == HttpStatus.NO_CONTENT.value()) { - action.andExpect(content().string(is(emptyOrNullString()))); + assertThat(action).body().isEmpty(); } if (!receiptLink.isPresent()) { @@ -342,19 +355,20 @@ private MockHttpServletResponse takeReceipt(MockHttpServletResponse response) th var receiptLink = getDiscovererFor(response).findRequiredLinkWithRel(RECEIPT_REL, response.getContentAsString()); - var receiptResponse = mvc.perform(get(receiptLink.getHref())). // - andExpect(status().isOk()). // - andReturn().getResponse(); + var result = mvc.perform(get(receiptLink.getHref())); + + assertThat(result).hasStatus(HttpStatus.OK); + + var receiptResponse = result.getMvcResult().getResponse(); LOG.info("Accessing receipt, got:" + receiptResponse.getContentAsString()); LOG.info("Taking receipt…"); - return mvc.perform( // - delete(receiptLink.getHref()).// - accept(MediaTypes.HAL_JSON)) - . // - andExpect(status().isOk()). // - andReturn().getResponse(); + result = mvc.perform(delete(receiptLink.getHref()).accept(MediaTypes.HAL_JSON)); + + assertThat(result).hasStatus(HttpStatus.OK); + + return result.getMvcResult().getResponse(); } /** @@ -367,16 +381,17 @@ private MockHttpServletResponse takeReceipt(MockHttpServletResponse response) th private void verifyOrderTaken(MockHttpServletResponse response) throws Exception { var orderLink = getDiscovererFor(response).findRequiredLinkWithRel(ORDER_REL, response.getContentAsString()); - var orderResponse = mvc.perform(get(orderLink.expand().getHref())). // - andExpect(status().isOk()). // // - andExpect(linkWithRelIsPresent(IanaLinkRelations.SELF)). // - andExpect(linkWithRelIsNotPresent(UPDATE_REL)). // - andExpect(linkWithRelIsNotPresent(CANCEL_REL)). // - andExpect(linkWithRelIsNotPresent(PAYMENT_REL)). // - andExpect(jsonPath("$.status", is("Delivered"))). // - andReturn().getResponse(); - - LOG.info("Final order state: " + orderResponse.getContentAsString()); + var result = mvc.perform(get(orderLink.expand().getHref())); + + assertThat(result) + .hasStatus(HttpStatus.OK) + .has(linkWithRel(IanaLinkRelations.SELF)) + .doesNotHave(linkWithRel(UPDATE_REL)) + .doesNotHave(linkWithRel(CANCEL_REL)) + .doesNotHave(linkWithRel(PAYMENT_REL)) + .bodyJson().extractingPath("$.status").isEqualTo("Delivered"); + + LOG.info("Final order state: " + result.getMvcResult().getResponse().getContentAsString()); } /** @@ -393,8 +408,10 @@ private void cancelOrder(MockHttpServletResponse response) throws Exception { var selfLink = discoverer.findRequiredLinkWithRel(IanaLinkRelations.SELF, content); var cancellationLink = discoverer.findRequiredLinkWithRel(CANCEL_REL, content); - mvc.perform(delete(cancellationLink.getHref())).andExpect(status().isNoContent()); - mvc.perform(get(selfLink.getHref())).andExpect(status().isNotFound()); + assertThat(mvc.perform(delete(cancellationLink.getHref()))) + .hasStatus(HttpStatus.NO_CONTENT); + assertThat(mvc.perform(get(selfLink.getHref()))) + .hasStatus(HttpStatus.NOT_FOUND); } /** @@ -412,8 +429,9 @@ private MockHttpServletResponse verifyPaymentIsDocumented(MockHttpServletRespons var paymentCurie = curiesLink.expand(PAYMENT_REL.getLocalPart()); LOG.info(String.format("Expanded payment curie pointing to %s…", paymentCurie)); - mvc.perform(get(paymentCurie.getHref())). // - andExpect(status().isOk()); // + + assertThat(mvc.perform(get(paymentCurie.getHref()))) + .hasStatus(HttpStatus.OK); return response; }