From db16f279c268939cdd8e60e685867291cbef15d8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Jan 2025 15:06:43 +0000 Subject: [PATCH 01/15] Begin work on 4.0 --- .github/workflows/build-and-deploy-snapshot.yml | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-deploy-snapshot.yml b/.github/workflows/build-and-deploy-snapshot.yml index bbc755b2..8850cec1 100644 --- a/.github/workflows/build-and-deploy-snapshot.yml +++ b/.github/workflows/build-and-deploy-snapshot.yml @@ -25,7 +25,7 @@ jobs: with: artifact-properties: | /**/spring-restdocs-*.zip::zip.type=docs,zip.deployed=false - build-name: 'spring-restdocs-3.0.x' + build-name: 'spring-restdocs-4.0.x' folder: 'deployment-repository' password: ${{ secrets.ARTIFACTORY_PASSWORD }} repository: ${{ 'libs-snapshot-local' }} diff --git a/gradle.properties b/gradle.properties index 64a74adb..17c12f9f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.0.4-SNAPSHOT +version=4.0.0-SNAPSHOT org.gradle.caching=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 From c72e32d3cb6f2f6b08101b3f3481588409941e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 13 Jan 2025 13:33:40 +0100 Subject: [PATCH 02/15] Raise the minimum support version of Spring Framework to 7.0 See gh-955 --- gradle.properties | 2 +- spring-restdocs-core/build.gradle | 7 -- .../restdocs/cli/CliOperationRequest.java | 4 +- .../restdocs/cli/CurlRequestSnippet.java | 4 +- .../restdocs/cli/HttpieRequestSnippet.java | 4 +- .../headers/RequestHeadersSnippet.java | 4 +- .../headers/ResponseHeadersSnippet.java | 4 +- .../restdocs/http/HttpRequestSnippet.java | 4 +- .../restdocs/http/HttpResponseSnippet.java | 4 +- ...HeadersModifyingOperationPreprocessor.java | 4 +- .../UriModifyingOperationPreprocessor.java | 4 +- .../RestDocumentationConfigurerTests.java | 6 +- ...rsModifyingOperationPreprocessorTests.java | 83 +++++++++++-------- spring-restdocs-mockmvc/build.gradle | 7 -- .../mockmvc/MockMvcResponseConverter.java | 4 +- .../mockmvc/MockMvcRequestConverterTests.java | 7 +- .../MockMvcResponseConverterTests.java | 8 +- .../RestAssuredRequestConverterTests.java | 20 ++--- spring-restdocs-webtestclient/build.gradle | 7 -- .../WebTestClientResponseConverter.java | 4 +- .../WebTestClientRequestConverterTests.java | 11 +-- .../WebTestClientResponseConverterTests.java | 8 +- 22 files changed, 99 insertions(+), 111 deletions(-) diff --git a/gradle.properties b/gradle.properties index 17c12f9f..4c5eda76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,4 @@ org.gradle.parallel=true javaFormatVersion=0.0.43 jmustacheVersion=1.15 -springFrameworkVersion=6.1.13 +springFrameworkVersion=7.0.0-SNAPSHOT diff --git a/spring-restdocs-core/build.gradle b/spring-restdocs-core/build.gradle index a7cfb3a2..5face83b 100644 --- a/spring-restdocs-core/build.gradle +++ b/spring-restdocs-core/build.gradle @@ -82,10 +82,3 @@ components.java.withVariantsFromConfiguration(configurations.testFixturesApiElem components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } - -compatibilityTest { - dependency("Spring Framework") { springFramework -> - springFramework.groupId = "org.springframework" - springFramework.versions = ["6.0.+", "6.2.+"] - } -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java index a955fcaf..efc21ba4 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -76,7 +76,7 @@ public String getContentAsString() { @Override public HttpHeaders getHeaders() { HttpHeaders filteredHeaders = new HttpHeaders(); - for (Entry> header : this.delegate.getHeaders().entrySet()) { + for (Entry> header : this.delegate.getHeaders().headerSet()) { if (allowedHeader(header)) { filteredHeaders.put(header.getKey(), header.getValue()); } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java index 358db25a..469973ae 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -132,7 +132,7 @@ private void writeHttpMethod(OperationRequest request, StringBuilder builder) { } private void writeHeaders(CliOperationRequest request, List lines) { - for (Entry> entry : request.getHeaders().entrySet()) { + for (Entry> entry : request.getHeaders().headerSet()) { for (String header : entry.getValue()) { if (StringUtils.hasText(request.getContentAsString()) && HttpHeaders.CONTENT_TYPE.equals(entry.getKey()) && MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) { diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java index 18b33779..8b92fa0b 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -160,7 +160,7 @@ private void writeFormDataIfNecessary(OperationRequest request, List lin private void writeHeaders(OperationRequest request, List lines) { HttpHeaders headers = request.getHeaders(); - for (Entry> entry : headers.entrySet()) { + for (Entry> entry : headers.headerSet()) { if (entry.getKey().equals(HttpHeaders.CONTENT_TYPE) && headers.getContentType().isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) { continue; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java index e64e7c08..79d58792 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -57,7 +57,7 @@ protected RequestHeadersSnippet(List descriptors, Map extractActualHeaders(Operation operation) { - return operation.getRequest().getHeaders().keySet(); + return operation.getRequest().getHeaders().headerNames(); } /** diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java index e1acb681..ea9687e5 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -57,7 +57,7 @@ protected ResponseHeadersSnippet(List descriptors, Map extractActualHeaders(Operation operation) { - return operation.getResponse().getHeaders().keySet(); + return operation.getResponse().getHeaders().headerNames(); } /** diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java index 403b83be..87087b31 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -91,7 +91,7 @@ private boolean includeParametersInUri(OperationRequest request) { private List> getHeaders(OperationRequest request) { List> headers = new ArrayList<>(); - for (Entry> header : request.getHeaders().entrySet()) { + for (Entry> header : request.getHeaders().headerSet()) { for (String value : header.getValue()) { if (HttpHeaders.CONTENT_TYPE.equals(header.getKey()) && !request.getParts().isEmpty()) { headers.add(header(header.getKey(), String.format("%s; boundary=%s", value, MULTIPART_BOUNDARY))); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java index ec56d397..919d312f 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -73,7 +73,7 @@ private String responseBody(OperationResponse response) { private List> headers(OperationResponse response) { List> headers = new ArrayList<>(); - for (Entry> header : response.getHeaders().entrySet()) { + for (Entry> header : response.getHeaders().headerSet()) { List values = header.getValue(); for (String value : values) { headers.add(header(header.getKey(), value)); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java index 9f0d4372..001f7789 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -210,7 +210,7 @@ private RemoveHeadersByNamePatternModification(Pattern namePattern) { @Override public void applyTo(HttpHeaders headers) { - headers.keySet().removeIf((name) -> this.namePattern.matcher(name).matches()); + headers.headerNames().removeIf((name) -> this.namePattern.matcher(name).matches()); } } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java index b36d9715..6e15b74b 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -147,7 +147,7 @@ public OperationResponse preprocess(OperationResponse response) { private HttpHeaders modify(HttpHeaders headers) { HttpHeaders modified = new HttpHeaders(); - for (Entry> header : headers.entrySet()) { + for (Entry> header : headers.headerSet()) { for (String value : header.getValue()) { modified.add(header.getKey(), this.contentModifier.modify(value)); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java index 67f59054..07fae9e5 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -210,7 +210,7 @@ public void customDefaultOperationRequestPreprocessor() { headers.add("Foo", "value"); OperationRequest request = new OperationRequestFactory().create(URI.create("http://localhost:8080/"), HttpMethod.GET, null, headers, null, Collections.emptyList()); - assertThat(preprocessor.preprocess(request).getHeaders()).doesNotContainKey("Foo"); + assertThat(preprocessor.preprocess(request).getHeaders().headerNames()).doesNotContain("Foo"); } @Test @@ -224,7 +224,7 @@ public void customDefaultOperationResponsePreprocessor() { HttpHeaders headers = new HttpHeaders(); headers.add("Foo", "value"); OperationResponse response = new OperationResponseFactory().create(HttpStatus.OK, headers, null); - assertThat(preprocessor.preprocess(response).getHeaders()).doesNotContainKey("Foo"); + assertThat(preprocessor.preprocess(response).getHeaders().headerNames()).doesNotContain("Foo"); } private RestDocumentationContext createContext() { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java index 9091da49..5d3e77ac 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -33,6 +33,7 @@ import org.springframework.restdocs.operation.OperationResponseFactory; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * Tests for {@link HeadersModifyingOperationPreprocessor}. @@ -47,60 +48,66 @@ public class HeadersModifyingOperationPreprocessorTests { @Test public void addNewHeader() { this.preprocessor.add("a", "alpha"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders()).containsEntry("a", - Arrays.asList("alpha")); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders()).containsEntry("a", - Arrays.asList("alpha")); + assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().get("a")) + .isEqualTo(Arrays.asList("alpha")); + assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().get("a")) + .isEqualTo(Arrays.asList("alpha")); } @Test public void addValueToExistingHeader() { this.preprocessor.add("a", "alpha"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))).getHeaders()) - .containsEntry("a", Arrays.asList("apple", "alpha")); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))).getHeaders()) - .containsEntry("a", Arrays.asList("apple", "alpha")); + assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerSet()).contains(entry("a", Arrays.asList("apple", "alpha"))); + assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerSet()).contains(entry("a", Arrays.asList("apple", "alpha"))); } @Test public void setNewHeader() { this.preprocessor.set("a", "alpha", "avocado"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders()).containsEntry("a", - Arrays.asList("alpha", "avocado")); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders()).containsEntry("a", - Arrays.asList("alpha", "avocado")); + assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerSet()) + .contains(entry("a", Arrays.asList("alpha", "avocado"))); + assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerSet()) + .contains(entry("a", Arrays.asList("alpha", "avocado"))); } @Test public void setExistingHeader() { this.preprocessor.set("a", "alpha", "avocado"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))).getHeaders()) - .containsEntry("a", Arrays.asList("alpha", "avocado")); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))).getHeaders()) - .containsEntry("a", Arrays.asList("alpha", "avocado")); + assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerSet()).contains(entry("a", Arrays.asList("alpha", "avocado"))); + assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerSet()).contains(entry("a", Arrays.asList("alpha", "avocado"))); } @Test public void removeNonExistentHeader() { this.preprocessor.remove("a"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders()).doesNotContainKey("a"); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders()).doesNotContainKey("a"); + assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerNames()).doesNotContain("a"); + assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerNames()).doesNotContain("a"); } @Test public void removeHeader() { this.preprocessor.remove("a"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))).getHeaders()) - .doesNotContainKey("a"); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))).getHeaders()) - .doesNotContainKey("a"); + assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerNames()).doesNotContain("a"); + assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerNames()).doesNotContain("a"); } @Test public void removeHeaderValueForNonExistentHeader() { this.preprocessor.remove("a", "apple"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders()).doesNotContainKey("a"); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders()).doesNotContainKey("a"); + assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerNames()).doesNotContain("a"); + assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerNames()).doesNotContain("a"); } @Test @@ -108,20 +115,24 @@ public void removeHeaderValueWithMultipleValues() { this.preprocessor.remove("a", "apple"); assertThat( this.preprocessor.preprocess(createRequest((headers) -> headers.addAll("a", List.of("apple", "alpha")))) - .getHeaders()) - .containsEntry("a", Arrays.asList("alpha")); + .getHeaders() + .headerSet()) + .contains(entry("a", Arrays.asList("alpha"))); assertThat(this.preprocessor .preprocess(createResponse((headers) -> headers.addAll("a", List.of("apple", "alpha")))) - .getHeaders()).containsEntry("a", Arrays.asList("alpha")); + .getHeaders() + .headerSet()).contains(entry("a", Arrays.asList("alpha"))); } @Test public void removeHeaderValueWithSingleValueRemovesEntryEntirely() { this.preprocessor.remove("a", "apple"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))).getHeaders()) - .doesNotContainKey("a"); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))).getHeaders()) - .doesNotContainKey("a"); + assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerNames()).doesNotContain("a"); + assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) + .getHeaders() + .headerNames()).doesNotContain("a"); } @Test @@ -133,10 +144,10 @@ public void removeHeadersByNamePattern() { headers.add("bravo", "bravo"); }; this.preprocessor.removeMatching("^a.*"); - assertThat(this.preprocessor.preprocess(createRequest(headersCustomizer)).getHeaders()).containsOnlyKeys("Host", - "bravo"); - assertThat(this.preprocessor.preprocess(createResponse(headersCustomizer)).getHeaders()) - .containsOnlyKeys("bravo"); + assertThat(this.preprocessor.preprocess(createRequest(headersCustomizer)).getHeaders().headerNames()) + .containsOnly("Host", "bravo"); + assertThat(this.preprocessor.preprocess(createResponse(headersCustomizer)).getHeaders().headerNames()) + .containsOnly("bravo"); } private OperationRequest createRequest() { diff --git a/spring-restdocs-mockmvc/build.gradle b/spring-restdocs-mockmvc/build.gradle index 8a487c2b..1f4ed7d7 100644 --- a/spring-restdocs-mockmvc/build.gradle +++ b/spring-restdocs-mockmvc/build.gradle @@ -22,10 +22,3 @@ dependencies { testImplementation("org.hamcrest:hamcrest-library") testImplementation("org.mockito:mockito-core") } - -compatibilityTest { - dependency("Spring Framework") { springFramework -> - springFramework.groupId = "org.springframework" - springFramework.versions = ["6.0.+", "6.2.+"] - } -} \ No newline at end of file diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java index e3caca60..701d8699 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -68,7 +68,7 @@ private HttpHeaders extractHeaders(MockHttpServletResponse response) { } } - if (response.getCookies() != null && !headers.containsKey(HttpHeaders.SET_COOKIE)) { + if (response.getCookies() != null && !headers.containsHeader(HttpHeaders.SET_COOKIE)) { for (Cookie cookie : response.getCookies()) { headers.add(HttpHeaders.SET_COOKIE, generateSetCookieHeader(cookie)); } diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java index 397b7ed7..5122510a 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -37,6 +37,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -78,8 +79,8 @@ public void requestWithHeaders() { MockMvcRequestBuilders.get("/foo").header("a", "alpha", "apple").header("b", "bravo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - assertThat(request.getHeaders()).containsEntry("a", Arrays.asList("alpha", "apple")); - assertThat(request.getHeaders()).containsEntry("b", Arrays.asList("bravo")); + assertThat(request.getHeaders().headerSet()).contains(entry("a", Arrays.asList("alpha", "apple")), + entry("b", Arrays.asList("bravo"))); } @Test diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java index 1d208b43..300649a2 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -30,6 +30,7 @@ import org.springframework.restdocs.operation.ResponseCookie; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * Tests for {@link MockMvcResponseConverter}. @@ -57,9 +58,8 @@ public void responseWithCookie() { cookie.setHttpOnly(true); response.addCookie(cookie); OperationResponse operationResponse = this.factory.convert(response); - assertThat(operationResponse.getHeaders()).hasSize(1); - assertThat(operationResponse.getHeaders()).containsEntry(HttpHeaders.SET_COOKIE, - Collections.singletonList("name=value; Domain=localhost; HttpOnly")); + assertThat(operationResponse.getHeaders().headerSet()).containsOnly( + entry(HttpHeaders.SET_COOKIE, Collections.singletonList("name=value; Domain=localhost; HttpOnly"))); assertThat(operationResponse.getCookies()).hasSize(1); assertThat(operationResponse.getCookies()).first().extracting(ResponseCookie::getName).isEqualTo("name"); assertThat(operationResponse.getCookies()).first().extracting(ResponseCookie::getValue).isEqualTo("value"); diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java index 0307e20b..e7abf05d 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.entry; /** * Tests for {@link RestAssuredRequestConverter}. @@ -98,10 +99,8 @@ public void headers() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).header("Foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getHeaders()).hasSize(2); - assertThat(request.getHeaders()).containsEntry("Foo", Collections.singletonList("bar")); - assertThat(request.getHeaders()).containsEntry("Host", - Collections.singletonList("localhost:" + tomcat.getPort())); + assertThat(request.getHeaders().headerSet()).containsOnly(entry("Foo", Collections.singletonList("bar")), + entry("Host", Collections.singletonList("localhost:" + tomcat.getPort()))); } @Test @@ -112,11 +111,9 @@ public void headersWithCustomAccept() { .accept("application/json"); requestSpec.get("/"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getHeaders()).hasSize(3); - assertThat(request.getHeaders()).containsEntry("Foo", Collections.singletonList("bar")); - assertThat(request.getHeaders()).containsEntry("Accept", Collections.singletonList("application/json")); - assertThat(request.getHeaders()).containsEntry("Host", - Collections.singletonList("localhost:" + tomcat.getPort())); + assertThat(request.getHeaders().headerSet()).containsOnly(entry("Foo", Collections.singletonList("bar")), + entry("Accept", Collections.singletonList("application/json")), + entry("Host", Collections.singletonList("localhost:" + tomcat.getPort()))); } @Test @@ -153,8 +150,7 @@ public void multipart() { assertThat(parts).extracting("name").containsExactly("a", "b"); assertThat(parts).extracting("submittedFileName").containsExactly("a.txt", "file"); assertThat(parts).extracting("contentAsString").containsExactly("alpha", "{\"foo\":\"bar\"}"); - assertThat(parts).extracting("headers") - .extracting(HttpHeaders.CONTENT_TYPE) + assertThat(parts).map((part) -> part.getHeaders().get(HttpHeaders.CONTENT_TYPE)) .containsExactly(Collections.singletonList(MediaType.TEXT_PLAIN_VALUE), Collections.singletonList(MediaType.APPLICATION_JSON_VALUE)); } diff --git a/spring-restdocs-webtestclient/build.gradle b/spring-restdocs-webtestclient/build.gradle index 2e26c403..843d8995 100644 --- a/spring-restdocs-webtestclient/build.gradle +++ b/spring-restdocs-webtestclient/build.gradle @@ -21,10 +21,3 @@ dependencies { testRuntimeOnly("org.springframework:spring-context") } - -compatibilityTest { - dependency("Spring Framework") { springFramework -> - springFramework.groupId = "org.springframework" - springFramework.versions = ["6.0.+", "6.2.+"] - } -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java index 4aef283a..5a8416bd 100644 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java +++ b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -46,7 +46,7 @@ public OperationResponse convert(ExchangeResult result) { private HttpHeaders extractHeaders(ExchangeResult result) { HttpHeaders headers = result.getResponseHeaders(); - if (result.getResponseCookies().isEmpty() || headers.containsKey(HttpHeaders.SET_COOKIE)) { + if (result.getResponseCookies().isEmpty() || headers.containsHeader(HttpHeaders.SET_COOKIE)) { return headers; } result.getResponseCookies() diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java index edc578ba..5c8782e3 100644 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java +++ b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -39,6 +39,7 @@ import org.springframework.web.reactive.function.server.ServerResponse; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.POST; @@ -99,8 +100,8 @@ public void requestWithHeaders() { OperationRequest request = this.converter.convert(result); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - assertThat(request.getHeaders()).containsEntry("a", Arrays.asList("alpha", "apple")); - assertThat(request.getHeaders()).containsEntry("b", Arrays.asList("bravo")); + assertThat(request.getHeaders().headerSet()).contains(entry("a", Arrays.asList("alpha", "apple")), + entry("b", Arrays.asList("bravo"))); } @Test @@ -266,7 +267,7 @@ public void multipartUpload() { OperationRequestPart part = request.getParts().iterator().next(); assertThat(part.getName()).isEqualTo("file"); assertThat(part.getSubmittedFileName()).isNull(); - assertThat(part.getHeaders()).hasSize(2); + assertThat(part.getHeaders().size()).isEqualTo(2); assertThat(part.getHeaders().getContentLength()).isEqualTo(4L); assertThat(part.getHeaders().getContentDisposition().getName()).isEqualTo("file"); assertThat(part.getContent()).containsExactly(1, 2, 3, 4); @@ -303,7 +304,7 @@ public String getFilename() { OperationRequestPart part = request.getParts().iterator().next(); assertThat(part.getName()).isEqualTo("file"); assertThat(part.getSubmittedFileName()).isEqualTo("image.png"); - assertThat(part.getHeaders()).hasSize(3); + assertThat(part.getHeaders().size()).isEqualTo(3); assertThat(part.getHeaders().getContentLength()).isEqualTo(4); ContentDisposition contentDisposition = part.getHeaders().getContentDisposition(); assertThat(contentDisposition.getName()).isEqualTo("file"); diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java index e397975d..474c6feb 100644 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java +++ b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -32,6 +32,7 @@ import org.springframework.web.reactive.function.server.ServerResponse; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; /** @@ -83,9 +84,8 @@ public void responseWithCookie() { .expectBody() .returnResult(); OperationResponse response = this.converter.convert(result); - assertThat(response.getHeaders()).hasSize(1); - assertThat(response.getHeaders()).containsEntry(HttpHeaders.SET_COOKIE, - Collections.singletonList("name=value; Domain=localhost; HttpOnly")); + assertThat(response.getHeaders().headerSet()).containsOnly( + entry(HttpHeaders.SET_COOKIE, Collections.singletonList("name=value; Domain=localhost; HttpOnly"))); assertThat(response.getCookies()).hasSize(1); assertThat(response.getCookies()).first().extracting(ResponseCookie::getName).isEqualTo("name"); assertThat(response.getCookies()).first().extracting(ResponseCookie::getValue).isEqualTo("value"); From 34c93666e1b44e4c5554ef4c65d6e09b8db0fcba Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Jan 2025 15:18:02 +0000 Subject: [PATCH 03/15] Polish "Raise the minimum support version of Spring Framework to 7.0" See gh-955 --- docs/src/docs/asciidoc/documenting-your-api.adoc | 2 -- docs/src/docs/asciidoc/getting-started.adoc | 2 +- gradle.properties | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index ce3dc590..55fa120e 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -802,8 +802,6 @@ Uses the static `parameterWithName` method on `org.springframework.restdocs.requ The result is a snippet named `path-parameters.adoc` that contains a table describing the path parameters that are supported by the resource. -TIP: If you use MockMvc with Spring Framework 6.1 or earlier, to make the path parameters available for documentation, you must build the request by using one of the methods on `RestDocumentationRequestBuilders` rather than `MockMvcRequestBuilders`. - When documenting path parameters, the test fails if an undocumented path parameter is used in the request. Similarly, the test also fails if a documented path parameter is not found in the request and the path parameter has not been marked as optional. diff --git a/docs/src/docs/asciidoc/getting-started.adoc b/docs/src/docs/asciidoc/getting-started.adoc index 81e24381..3e4e2c4f 100644 --- a/docs/src/docs/asciidoc/getting-started.adoc +++ b/docs/src/docs/asciidoc/getting-started.adoc @@ -18,7 +18,7 @@ If you want to jump straight in, a number of https://github.com/spring-projects/ Spring REST Docs has the following minimum requirements: * Java 17 -* Spring Framework 6 +* Spring Framework 7 Additionally, the `spring-restdocs-restassured` module requires REST Assured 5.2. diff --git a/gradle.properties b/gradle.properties index 4c5eda76..5cc79413 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,4 @@ org.gradle.parallel=true javaFormatVersion=0.0.43 jmustacheVersion=1.15 -springFrameworkVersion=7.0.0-SNAPSHOT +springFrameworkVersion=7.0.0-M1 From 5e6530a601485348267776ba20272423208601a6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Jan 2025 16:06:01 +0000 Subject: [PATCH 04/15] Raise minimum versions of validation dependencies Closes gh-956 --- build.gradle | 4 +- .../docs/asciidoc/documenting-your-api.adoc | 2 +- ...ceBundleConstraintDescriptionResolver.java | 10 ++-- .../DefaultConstraintDescriptions.properties | 3 -- ...dleConstraintDescriptionResolverTests.java | 54 ++++++++++--------- spring-restdocs-platform/build.gradle | 4 +- 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/build.gradle b/build.gradle index 42e043b3..b58d8013 100644 --- a/build.gradle +++ b/build.gradle @@ -35,8 +35,8 @@ nohttp { ext { javadocLinks = [ "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/", - "https://docs.jboss.org/hibernate/validator/7.0/api/", - "https://jakarta.ee/specifications/bean-validation/3.0/apidocs/" + "https://docs.jboss.org/hibernate/validator/9.0/api/", + "https://jakarta.ee/specifications/bean-validation/3.1/apidocs/" ] as String[] } diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index 55fa120e..cb9ce82a 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -1146,7 +1146,7 @@ To take complete control of constraint resolution, you can use your own implemen [[documenting-your-api-constraints-describing]] ==== Describing Constraints -Default descriptions are provided for all of Bean Validation 3.0's constraints: +Default descriptions are provided for all of Bean Validation 3.1's constraints: * `AssertFalse` * `AssertTrue` diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java index 1c409a6f..59788d06 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -64,7 +64,7 @@ * {@code jakarta.validation.constraints.NotNull} is * {@code jakarta.validation.constraints.NotNull.description}. *

- * Default descriptions are provided for Bean Validation 2.0's constraints: + * Default descriptions are provided for all of Bean Validation 3.1's constraints: * *

    *
  • {@link AssertFalse} @@ -92,20 +92,18 @@ *
* *

- * Default descriptions are also provided for Hibernate Validator's constraints: + * Default descriptions are also provided for the following Hibernate Validator + * constraints: * *

    *
  • {@link CodePointLength} *
  • {@link CreditCardNumber} *
  • {@link Currency} *
  • {@link EAN} - *
  • {@link org.hibernate.validator.constraints.Email} *
  • {@link Length} *
  • {@link LuhnCheck} *
  • {@link Mod10Check} *
  • {@link Mod11Check} - *
  • {@link org.hibernate.validator.constraints.NotBlank} - *
  • {@link org.hibernate.validator.constraints.NotEmpty} *
  • {@link Range} *
  • {@link URL} *
diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties index 248cb238..699b900e 100644 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties @@ -24,12 +24,9 @@ org.hibernate.validator.constraints.CodePointLength.description=Code point lengt org.hibernate.validator.constraints.CreditCardNumber.description=Must be a well-formed credit card number org.hibernate.validator.constraints.Currency.description=Must be in an accepted currency unit (${value}) org.hibernate.validator.constraints.EAN.description=Must be a well-formed ${type} number -org.hibernate.validator.constraints.Email.description=Must be a well-formed email address org.hibernate.validator.constraints.Length.description=Length must be between ${min} and ${max} inclusive org.hibernate.validator.constraints.LuhnCheck.description=Must pass the Luhn Modulo 10 checksum algorithm org.hibernate.validator.constraints.Mod10Check.description=Must pass the Mod10 checksum algorithm org.hibernate.validator.constraints.Mod11Check.description=Must pass the Mod11 checksum algorithm -org.hibernate.validator.constraints.NotBlank.description=Must not be blank -org.hibernate.validator.constraints.NotEmpty.description=Must not be empty org.hibernate.validator.constraints.Range.description=Must be at least ${min} and at most ${max} org.hibernate.validator.constraints.URL.description=Must be a well-formed URL \ No newline at end of file diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java index 4598cd1a..f4b5e2af 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -21,9 +21,11 @@ import java.net.URL; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.ListResourceBundle; import java.util.ResourceBundle; +import java.util.Set; import javax.money.MonetaryAmount; @@ -61,7 +63,11 @@ import org.junit.Test; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -180,12 +186,6 @@ public void defaultMessageEmail() { assertThat(constraintDescriptionForField("email")).isEqualTo("Must be a well-formed email address"); } - @Test - public void defaultMessageEmailHibernateValidator() { - assertThat(constraintDescriptionForField("emailHibernateValidator")) - .isEqualTo("Must be a well-formed email address"); - } - @Test public void defaultMessageLength() { assertThat(constraintDescriptionForField("length")).isEqualTo("Length must be between 2 and 10 inclusive"); @@ -222,11 +222,6 @@ public void defaultMessageNotBlank() { assertThat(constraintDescriptionForField("notBlank")).isEqualTo("Must not be blank"); } - @Test - public void defaultMessageNotBlankHibernateValidator() { - assertThat(constraintDescriptionForField("notBlankHibernateValidator")).isEqualTo("Must not be blank"); - } - @Test public void defaultMessageNotEmpty() { assertThat(constraintDescriptionForField("notEmpty")).isEqualTo("Must not be empty"); @@ -298,6 +293,29 @@ protected Object[][] getContents() { assertThat(description).isEqualTo("Not null"); } + @Test + public void allBeanValidationConstraintsAreTested() throws Exception { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("jakarta/validation/constraints/*.class"); + Set> beanValidationConstraints = new HashSet<>(); + for (Resource resource : resources) { + String className = ClassUtils.convertResourcePathToClassName(((ClassPathResource) resource).getPath()); + if (className.endsWith(".class")) { + className = className.substring(0, className.length() - 6); + } + Class type = Class.forName(className); + if (type.isAnnotation() && type.isAnnotationPresent(jakarta.validation.Constraint.class)) { + beanValidationConstraints.add(type); + } + } + ReflectionUtils.doWithFields(Constrained.class, (field) -> { + for (Annotation annotation : field.getAnnotations()) { + beanValidationConstraints.remove(annotation.annotationType()); + } + }); + assertThat(beanValidationConstraints).isEmpty(); + } + private String constraintDescriptionForField(String name) { return this.resolver.resolveDescription(getConstraintFromField(name)); } @@ -372,10 +390,6 @@ private static final class Constrained { @Email private String email; - @SuppressWarnings("deprecation") - @org.hibernate.validator.constraints.Email - private String emailHibernateValidator; - @Length(min = 2, max = 10) private String length; @@ -397,17 +411,9 @@ private static final class Constrained { @NotBlank private String notBlank; - @SuppressWarnings("deprecation") - @org.hibernate.validator.constraints.NotBlank - private String notBlankHibernateValidator; - @NotEmpty private String notEmpty; - @SuppressWarnings("deprecation") - @org.hibernate.validator.constraints.NotEmpty - private String notEmptyHibernateValidator; - @Positive private int positive; diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle index 973cf732..4b2637cf 100644 --- a/spring-restdocs-platform/build.gradle +++ b/spring-restdocs-platform/build.gradle @@ -10,7 +10,7 @@ dependencies { constraints { api("com.samskivert:jmustache:$jmustacheVersion") api("jakarta.servlet:jakarta.servlet-api:6.0.0") - api("jakarta.validation:jakarta.validation-api:3.0.0") + api("jakarta.validation:jakarta.validation-api:3.1.0") api("junit:junit:4.13.1") api("org.apache.pdfbox:pdfbox:2.0.27") api("org.apache.tomcat.embed:tomcat-embed-core:10.1.1") @@ -20,7 +20,7 @@ dependencies { api("org.assertj:assertj-core:3.23.1") api("org.hamcrest:hamcrest-core:1.3") api("org.hamcrest:hamcrest-library:1.3") - api("org.hibernate.validator:hibernate-validator:8.0.0.Final") + api("org.hibernate.validator:hibernate-validator:9.0.0.CR1") api("org.javamoney:moneta:1.4.2") api("org.junit.jupiter:junit-jupiter-api:5.0.0") } From 85ebc4eb862b7a26be48a4ea0731950a21cd7c22 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Jan 2025 16:06:12 +0000 Subject: [PATCH 05/15] Align Servlet-related dependencies with Framework 7 baseline See gh-955 --- spring-restdocs-platform/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle index 4b2637cf..c3d3dcbf 100644 --- a/spring-restdocs-platform/build.gradle +++ b/spring-restdocs-platform/build.gradle @@ -9,12 +9,12 @@ javaPlatform { dependencies { constraints { api("com.samskivert:jmustache:$jmustacheVersion") - api("jakarta.servlet:jakarta.servlet-api:6.0.0") + api("jakarta.servlet:jakarta.servlet-api:6.1.0") api("jakarta.validation:jakarta.validation-api:3.1.0") api("junit:junit:4.13.1") api("org.apache.pdfbox:pdfbox:2.0.27") - api("org.apache.tomcat.embed:tomcat-embed-core:10.1.1") - api("org.apache.tomcat.embed:tomcat-embed-el:10.1.1") + api("org.apache.tomcat.embed:tomcat-embed-core:11.0.2") + api("org.apache.tomcat.embed:tomcat-embed-el:11.0.2") api("org.asciidoctor:asciidoctorj:2.5.7") api("org.asciidoctor:asciidoctorj-pdf:2.3.3") api("org.assertj:assertj-core:3.23.1") From 0b103fdc0e747cc935f1e7fecf6f19bfadda2ef2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 30 Jan 2025 16:28:44 +0000 Subject: [PATCH 06/15] Raise the minimum supported version of AsciidoctorJ to 3.0 Closes gh-957 --- docs/src/docs/asciidoc/getting-started.adoc | 2 + .../asciidoc/working-with-asciidoctor.adoc | 1 + spring-restdocs-asciidoctor/build.gradle | 9 --- .../DefaultAttributesPreprocessor.java | 41 ++++++++++++ .../RestDocsExtensionRegistry.java | 6 +- .../SnippetsDirectoryResolver.java | 11 +-- .../extensions/default_attributes.rb | 14 ---- .../DefaultAttributesPreprocessorTests.java | 67 +++++++++++++++++++ spring-restdocs-platform/build.gradle | 4 +- 9 files changed, 118 insertions(+), 37 deletions(-) create mode 100644 spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java delete mode 100644 spring-restdocs-asciidoctor/src/main/resources/extensions/default_attributes.rb create mode 100644 spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java diff --git a/docs/src/docs/asciidoc/getting-started.adoc b/docs/src/docs/asciidoc/getting-started.adoc index 3e4e2c4f..be21ed4f 100644 --- a/docs/src/docs/asciidoc/getting-started.adoc +++ b/docs/src/docs/asciidoc/getting-started.adoc @@ -78,6 +78,7 @@ If you want to use `WebTestClient` or REST Assured rather than MockMvc, add a de <4> Add `spring-restdocs-asciidoctor` as a dependency of the Asciidoctor plugin. This will automatically configure the `snippets` attribute for use in your `.adoc` files to point to `target/generated-snippets`. It will also allow you to use the `operation` block macro. +It requires AsciidoctorJ 3.0. [source,indent=0,subs="verbatim,attributes",role="secondary"] .Gradle @@ -114,6 +115,7 @@ It will also allow you to use the `operation` block macro. <3> Add a dependency on `spring-restdocs-asciidoctor` in the `asciidoctorExt` configuration. This will automatically configure the `snippets` attribute for use in your `.adoc` files to point to `build/generated-snippets`. It will also allow you to use the `operation` block macro. +It requires AsciidoctorJ 3.0. <4> Add a dependency on `spring-restdocs-mockmvc` in the `testImplementation` configuration. If you want to use `WebTestClient` or REST Assured rather than MockMvc, add a dependency on `spring-restdocs-webtestclient` or `spring-restdocs-restassured` respectively instead. <5> Configure a `snippetsDir` property that defines the output location for generated snippets. diff --git a/docs/src/docs/asciidoc/working-with-asciidoctor.adoc b/docs/src/docs/asciidoc/working-with-asciidoctor.adoc index 06859038..698d5e46 100644 --- a/docs/src/docs/asciidoc/working-with-asciidoctor.adoc +++ b/docs/src/docs/asciidoc/working-with-asciidoctor.adoc @@ -28,6 +28,7 @@ This section covers how to include Asciidoc snippets. You can use a macro named `operation` to import all or some of the snippets that have been generated for a specific operation. It is made available by including `spring-restdocs-asciidoctor` in your project's <>. +`spring-restdocs-asciidoctor` requires AsciidoctorJ 3.0. The target of the macro is the name of the operation. In its simplest form, you can use the macro to include all of the snippets for an operation, as shown in the following example: diff --git a/spring-restdocs-asciidoctor/build.gradle b/spring-restdocs-asciidoctor/build.gradle index 717744ea..ceb5202f 100644 --- a/spring-restdocs-asciidoctor/build.gradle +++ b/spring-restdocs-asciidoctor/build.gradle @@ -1,7 +1,6 @@ plugins { id "java-library" id "maven-publish" - id "io.spring.compatibility-test" version "0.0.3" } description = "Spring REST Docs Asciidoctor Extension" @@ -20,11 +19,3 @@ dependencies { testRuntimeOnly("org.asciidoctor:asciidoctorj-pdf") } - -compatibilityTest { - dependency("AsciidoctorJ") { asciidoctorj -> - asciidoctorj.groupId = "org.asciidoctor" - asciidoctorj.artifactId = "asciidoctorj" - asciidoctorj.versions = ["3.0.0"] - } -} \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java new file mode 100644 index 00000000..7fa6ce5d --- /dev/null +++ b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.asciidoctor; + +import org.asciidoctor.ast.Document; +import org.asciidoctor.extension.Preprocessor; +import org.asciidoctor.extension.PreprocessorReader; +import org.asciidoctor.extension.Reader; + +/** + * {@link Preprocessor} that sets defaults for REST Docs-related {@link Document} + * attributes. + * + * @author Andy Wilkinson + */ +final class DefaultAttributesPreprocessor extends Preprocessor { + + private final SnippetsDirectoryResolver snippetsDirectoryResolver = new SnippetsDirectoryResolver(); + + @Override + public Reader process(Document document, PreprocessorReader reader) { + document.setAttribute("snippets", this.snippetsDirectoryResolver.getSnippetsDirectory(document.getAttributes()), + false); + return reader; + } + +} diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java index 5432b23c..0a58a7cf 100644 --- a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java +++ b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2023 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. @@ -28,9 +28,7 @@ public final class RestDocsExtensionRegistry implements ExtensionRegistry { @Override public void register(Asciidoctor asciidoctor) { - asciidoctor.rubyExtensionRegistry() - .loadClass(RestDocsExtensionRegistry.class.getResourceAsStream("/extensions/default_attributes.rb")) - .preprocessor("DefaultAttributes"); + asciidoctor.javaExtensionRegistry().preprocessor(new DefaultAttributesPreprocessor()); asciidoctor.rubyExtensionRegistry() .loadClass(RestDocsExtensionRegistry.class.getResourceAsStream("/extensions/operation_block_macro.rb")) .blockMacro("operation", "OperationBlockMacro"); diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java index 521c0832..dfbb957a 100644 --- a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java +++ b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2021 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. @@ -29,14 +29,9 @@ * * @author Andy Wilkinson */ -public class SnippetsDirectoryResolver { +class SnippetsDirectoryResolver { - /** - * Returns the snippets directory derived from the given {@code attributes}. - * @param attributes the attributes - * @return the snippets directory - */ - public File getSnippetsDirectory(Map attributes) { + File getSnippetsDirectory(Map attributes) { if (System.getProperty("maven.home") != null) { return getMavenSnippetsDirectory(attributes); } diff --git a/spring-restdocs-asciidoctor/src/main/resources/extensions/default_attributes.rb b/spring-restdocs-asciidoctor/src/main/resources/extensions/default_attributes.rb deleted file mode 100644 index a4060d5b..00000000 --- a/spring-restdocs-asciidoctor/src/main/resources/extensions/default_attributes.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'asciidoctor/extensions' -require 'java' - -class DefaultAttributes < Asciidoctor::Extensions::Preprocessor - - def process(document, reader) - resolver = org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.new() - attributes = document.attributes - attributes["snippets"] = resolver.getSnippetsDirectory(attributes) unless attributes.has_key?("snippets") - false - end - -end - \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java new file mode 100644 index 00000000..78ba919c --- /dev/null +++ b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.asciidoctor; + +import java.io.File; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Attributes; +import org.asciidoctor.Options; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DefaultAttributesPreprocessor}. + * + * @author Andy Wilkinson + */ +public class DefaultAttributesPreprocessorTests { + + @Test + public void snippetsAttributeIsSet() { + String converted = createAsciidoctor().convert("{snippets}", createOptions("projectdir=../../..")); + assertThat(converted).contains("build" + File.separatorChar + "generated-snippets"); + } + + @Test + public void snippetsAttributeFromConvertArgumentIsNotOverridden() { + String converted = createAsciidoctor().convert("{snippets}", + createOptions("snippets=custom projectdir=../../..")); + assertThat(converted).contains("custom"); + } + + @Test + public void snippetsAttributeFromDocumentPreambleIsNotOverridden() { + String converted = createAsciidoctor().convert(":snippets: custom\n{snippets}", + createOptions("projectdir=../../..")); + assertThat(converted).contains("custom"); + } + + private Options createOptions(String attributes) { + Options options = Options.builder().build(); + options.setAttributes(Attributes.builder().arguments(attributes).build()); + return options; + } + + private Asciidoctor createAsciidoctor() { + Asciidoctor asciidoctor = Asciidoctor.Factory.create(); + asciidoctor.javaExtensionRegistry().preprocessor(new DefaultAttributesPreprocessor()); + return asciidoctor; + } + +} diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle index c3d3dcbf..28f64f14 100644 --- a/spring-restdocs-platform/build.gradle +++ b/spring-restdocs-platform/build.gradle @@ -15,8 +15,8 @@ dependencies { api("org.apache.pdfbox:pdfbox:2.0.27") api("org.apache.tomcat.embed:tomcat-embed-core:11.0.2") api("org.apache.tomcat.embed:tomcat-embed-el:11.0.2") - api("org.asciidoctor:asciidoctorj:2.5.7") - api("org.asciidoctor:asciidoctorj-pdf:2.3.3") + api("org.asciidoctor:asciidoctorj:3.0.0") + api("org.asciidoctor:asciidoctorj-pdf:2.3.19") api("org.assertj:assertj-core:3.23.1") api("org.hamcrest:hamcrest-core:1.3") api("org.hamcrest:hamcrest-library:1.3") From 8fd536c637e0c783a7411b545a766526ca24a7e2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 3 Feb 2025 10:00:28 +0000 Subject: [PATCH 07/15] Remove commons-logging excludes The spring-jcl module has been removed in Spring Framework 7 in favor of Commons Logging 1.3. This commit removes the excludes for commons-logging:commons-logging that werew required to ensure that spring-jcl was used instead but that are no longer needed. See gh-955 --- spring-restdocs-asciidoctor/build.gradle | 4 +--- spring-restdocs-restassured/build.gradle | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spring-restdocs-asciidoctor/build.gradle b/spring-restdocs-asciidoctor/build.gradle index ceb5202f..149c81e2 100644 --- a/spring-restdocs-asciidoctor/build.gradle +++ b/spring-restdocs-asciidoctor/build.gradle @@ -11,9 +11,7 @@ dependencies { internal(platform(project(":spring-restdocs-platform"))) testImplementation("junit:junit") - testImplementation("org.apache.pdfbox:pdfbox") { - exclude group: "commons-logging", module: "commons-logging" - } + testImplementation("org.apache.pdfbox:pdfbox") testImplementation("org.assertj:assertj-core") testImplementation("org.springframework:spring-core") diff --git a/spring-restdocs-restassured/build.gradle b/spring-restdocs-restassured/build.gradle index 812ed5d1..5c2dc4c9 100644 --- a/spring-restdocs-restassured/build.gradle +++ b/spring-restdocs-restassured/build.gradle @@ -8,9 +8,7 @@ description = "Spring REST Docs REST Assured" dependencies { api(project(":spring-restdocs-core")) - api("io.rest-assured:rest-assured") { - exclude group: "commons-logging", module: "commons-logging" - } + api("io.rest-assured:rest-assured") implementation("org.springframework:spring-web") internal(platform(project(":spring-restdocs-platform"))) From 64494bec84122111ec25f947d12ddf65d1fe7b9f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 16 May 2025 10:07:18 +0100 Subject: [PATCH 08/15] Add Git hooks for forward merges --- git/hooks/forward-merge | 17 ++++++++++++++--- git/hooks/prepare-forward-merge | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/git/hooks/forward-merge b/git/hooks/forward-merge index acbb6008..a042bb46 100755 --- a/git/hooks/forward-merge +++ b/git/hooks/forward-merge @@ -18,7 +18,14 @@ class ForwardMerge end def find_forward_merges(message_file) + $log.debug "Searching for forward merge" + branch=`git rev-parse -q --abbrev-ref HEAD`.strip + $log.debug "Found #{branch} from git rev-parse --abbrev-ref" + if( branch == "docs-build") then + $log.debug "Skipping docs build" + return nil + end rev=`git rev-parse -q --verify MERGE_HEAD`.strip $log.debug "Found #{rev} from git rev-parse" return nil unless rev @@ -65,7 +72,7 @@ def find_milestone(username, password, repository, title) prefix = title.delete_suffix('.x') $log.debug "Finding nearest milestone from candidates starting with #{prefix}" titles = milestones.map { |milestone| milestone['title'] } - titles = titles.select{ |title| title.start_with?(prefix) unless title.end_with?('.x')} + titles = titles.select{ |title| title.start_with?(prefix) unless title.end_with?('.x') || (title.count('.') > 2)} titles = titles.sort_by { |v| Gem::Version.new(v) } $log.debug "Considering candidates #{titles}" if(titles.empty?) @@ -112,12 +119,16 @@ message_file=ARGV[0] forward_merges = find_forward_merges(message_file) exit 0 unless forward_merges -$log.debug "Loading config from ~/.spring-restdocs/forward_merge.yml" +$log.debug "Loading config from ~/.spring-restdocs/forward-merge.yml" config = YAML.load_file(File.join(Dir.home, '.spring-restdocs', 'forward-merge.yml')) username = config['github']['credentials']['username'] password = config['github']['credentials']['password'] dry_run = config['dry_run'] -repository = 'spring-projects/spring-restdocs' + +gradleProperties = IO.read('gradle.properties') +springBuildType = gradleProperties.match(/^spring\.build-type\s?=\s?(.*)$/) +repository = (springBuildType && springBuildType[1] != 'oss') ? "spring-projects/spring-restdocs-#{springBuildType[1]}" : "spring-projects/spring-restdocs"; +$log.debug "Targeting repository #{repository}" forward_merges.each do |forward_merge| existing_issue = get_issue(username, password, repository, forward_merge.issue) diff --git a/git/hooks/prepare-forward-merge b/git/hooks/prepare-forward-merge index 2b049790..fbdb1e19 100755 --- a/git/hooks/prepare-forward-merge +++ b/git/hooks/prepare-forward-merge @@ -4,7 +4,7 @@ require 'net/http' require 'yaml' require 'logger' -$main_branch = "3.0.x" +$main_branch = "4.0.x" $log = Logger.new(STDOUT) $log.level = Logger::WARN From c7bde714d6c68fefe7c19c6dd841ac644cdf9207 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Jun 2025 16:47:20 +0100 Subject: [PATCH 09/15] Migrate tests to JUnit 5 Closes gh-959 --- spring-restdocs-asciidoctor/build.gradle | 7 +- .../AbstractOperationBlockMacroTests.java | 65 +- .../DefaultAttributesPreprocessorTests.java | 10 +- .../GradleOperationBlockMacroTests.java | 25 +- .../MavenOperationBlockMacroTests.java | 20 +- .../SnippetsDirectoryResolverTests.java | 19 +- spring-restdocs-core/build.gradle | 20 +- .../restdocs/snippet/TemplatedSnippet.java | 6 +- .../restdocs/AbstractSnippetTests.java | 34 +- .../RestDocumentationGeneratorTests.java | 16 +- .../ConcatenatingCommandFormatterTests.java | 14 +- .../restdocs/cli/CurlRequestSnippetTests.java | 279 +++---- .../cli/HttpieRequestSnippetTests.java | 291 ++++---- .../RestDocumentationConfigurerTests.java | 26 +- .../ConstraintDescriptionsTests.java | 10 +- ...dleConstraintDescriptionResolverTests.java | 76 +- .../ValidatorConstraintResolverTests.java | 14 +- .../RequestCookiesSnippetFailureTests.java | 59 -- .../cookies/RequestCookiesSnippetTests.java | 152 ++-- .../ResponseCookiesSnippetFailureTests.java | 60 -- .../cookies/ResponseCookiesSnippetTests.java | 152 ++-- .../RequestHeadersSnippetFailureTests.java | 59 -- .../headers/RequestHeadersSnippetTests.java | 160 ++--- .../ResponseHeadersSnippetFailureTests.java | 60 -- .../headers/ResponseHeadersSnippetTests.java | 152 ++-- .../http/HttpRequestSnippetTests.java | 228 +++--- .../http/HttpResponseSnippetTests.java | 95 ++- .../ContentTypeLinkExtractorTests.java | 12 +- .../LinkExtractorsPayloadTests.java | 32 +- .../hypermedia/LinksSnippetFailureTests.java | 80 --- .../hypermedia/LinksSnippetTests.java | 176 +++-- ...ntModifyingOperationPreprocessorTests.java | 12 +- ...tingOperationRequestPreprocessorTests.java | 8 +- ...ingOperationResponsePreprocessorTests.java | 8 +- ...rsModifyingOperationPreprocessorTests.java | 24 +- .../LinkMaskingContentModifierTests.java | 18 +- .../PatternReplacingContentModifierTests.java | 14 +- .../PrettyPrintingContentModifierTests.java | 31 +- ...riModifyingOperationPreprocessorTests.java | 68 +- .../AsciidoctorRequestFieldsSnippetTests.java | 58 +- ...ldPathPayloadSubsectionExtractorTests.java | 36 +- .../payload/FieldTypeResolverTests.java | 4 +- .../payload/JsonContentHandlerTests.java | 44 +- .../restdocs/payload/JsonFieldPathTests.java | 52 +- .../restdocs/payload/JsonFieldPathsTests.java | 24 +- .../payload/JsonFieldProcessorTests.java | 98 +-- .../JsonFieldTypesDiscovererTests.java | 56 +- .../restdocs/payload/JsonFieldTypesTests.java | 16 +- .../payload/PayloadDocumentationTests.java | 18 +- .../payload/RequestBodyPartSnippetTests.java | 116 ++- .../payload/RequestBodySnippetTests.java | 106 ++- .../RequestFieldsSnippetFailureTests.java | 196 ----- .../payload/RequestFieldsSnippetTests.java | 550 ++++++++------ .../RequestPartFieldsSnippetFailureTests.java | 70 -- .../RequestPartFieldsSnippetTests.java | 124 ++-- .../payload/ResponseBodySnippetTests.java | 112 ++- .../ResponseFieldsSnippetFailureTests.java | 175 ----- .../payload/ResponseFieldsSnippetTests.java | 557 ++++++++------ .../payload/XmlContentHandlerTests.java | 4 +- .../FormParametersSnippetFailureTests.java | 68 -- .../request/FormParametersSnippetTests.java | 208 +++--- .../PathParametersSnippetFailureTests.java | 73 -- .../request/PathParametersSnippetTests.java | 246 ++++--- .../QueryParametersSnippetFailureTests.java | 68 -- .../request/QueryParametersSnippetTests.java | 203 +++--- .../RequestPartsSnippetFailureTests.java | 68 -- .../request/RequestPartsSnippetTests.java | 174 ++--- ...tationContextPlaceholderResolverTests.java | 32 +- .../snippet/StandardWriterResolverTests.java | 27 +- .../snippet/TemplatedSnippetTests.java | 38 +- ...StandardTemplateResourceResolverTests.java | 14 +- ...sciidoctorTableCellContentLambdaTests.java | 10 +- .../request-fields-with-title.snippet | 1 + .../testfixtures/GeneratedSnippets.java | 144 ---- .../testfixtures/OperationTestRule.java | 52 -- .../testfixtures/OutputCaptureRule.java | 86 --- .../jupiter/AssertableSnippets.java | 679 ++++++++++++++++++ .../{ => jupiter}/CapturedOutput.java | 11 +- .../{ => jupiter}/OperationBuilder.java | 37 +- .../{ => jupiter}/OutputCapture.java | 18 +- .../jupiter/OutputCaptureExtension.java | 112 +++ .../jupiter/RenderedSnippetTest.java | 78 ++ .../jupiter/RenderedSnippetTestExtension.java | 155 ++++ .../testfixtures/jupiter/SnippetTemplate.java | 46 ++ .../testfixtures/jupiter/SnippetTest.java | 47 ++ .../jupiter/SnippetTestExtension.java | 66 ++ spring-restdocs-mockmvc/build.gradle | 8 +- .../mockmvc/MockMvcRequestConverterTests.java | 34 +- .../MockMvcResponseConverterTests.java | 10 +- ...ckMvcRestDocumentationConfigurerTests.java | 47 +- ...kMvcRestDocumentationIntegrationTests.java | 103 +-- ...RestDocumentationRequestBuildersTests.java | 42 +- spring-restdocs-platform/build.gradle | 3 +- spring-restdocs-restassured/build.gradle | 9 +- .../RestAssuredParameterBehaviorTests.java | 52 +- .../RestAssuredRequestConverterTests.java | 50 +- .../RestAssuredResponseConverterTests.java | 8 +- ...suredRestDocumentationConfigurerTests.java | 28 +- ...uredRestDocumentationIntegrationTests.java | 105 ++- .../restdocs/restassured/TomcatServer.java | 63 +- spring-restdocs-webtestclient/build.gradle | 8 +- .../WebTestClientRequestConverterTests.java | 30 +- .../WebTestClientResponseConverterTests.java | 10 +- ...lientRestDocumentationConfigurerTests.java | 29 +- ...ientRestDocumentationIntegrationTests.java | 39 +- 105 files changed, 4297 insertions(+), 4150 deletions(-) delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetFailureTests.java delete mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/GeneratedSnippets.java delete mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationTestRule.java delete mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCaptureRule.java create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java rename spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/{ => jupiter}/CapturedOutput.java (77%) rename spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/{ => jupiter}/OperationBuilder.java (93%) rename spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/{ => jupiter}/OutputCapture.java (94%) create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java create mode 100644 spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java diff --git a/spring-restdocs-asciidoctor/build.gradle b/spring-restdocs-asciidoctor/build.gradle index 149c81e2..f9d1f685 100644 --- a/spring-restdocs-asciidoctor/build.gradle +++ b/spring-restdocs-asciidoctor/build.gradle @@ -10,10 +10,15 @@ dependencies { internal(platform(project(":spring-restdocs-platform"))) - testImplementation("junit:junit") testImplementation("org.apache.pdfbox:pdfbox") testImplementation("org.assertj:assertj-core") + testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.springframework:spring-core") testRuntimeOnly("org.asciidoctor:asciidoctorj-pdf") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks.named("test") { + useJUnitPlatform() } diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java index cfc43b55..d6df5f91 100644 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java +++ b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -35,11 +35,10 @@ import org.asciidoctor.Attributes; import org.asciidoctor.Options; import org.asciidoctor.SafeMode; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.springframework.util.FileSystemUtils; @@ -51,42 +50,42 @@ * @author Gerrit Meier * @author Andy Wilkinson */ -public abstract class AbstractOperationBlockMacroTests { +abstract class AbstractOperationBlockMacroTests { - @Rule - public TemporaryFolder temp = new TemporaryFolder(); + private final Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - private Options options; + @TempDir + protected File temp; - private final Asciidoctor asciidoctor = Asciidoctor.Factory.create(); + private Options options; - @Before - public void setUp() throws IOException { + @BeforeEach + void setUp() throws IOException { prepareOperationSnippets(getBuildOutputLocation()); this.options = Options.builder().safe(SafeMode.UNSAFE).baseDir(getSourceLocation()).build(); this.options.setAttributes(getAttributes()); CapturingLogHandler.clear(); } - @After - public void verifyLogging() { + @AfterEach + void verifyLogging() { assertThat(CapturingLogHandler.getLogRecords()).isEmpty(); } - public void prepareOperationSnippets(File buildOutputLocation) throws IOException { + private void prepareOperationSnippets(File buildOutputLocation) throws IOException { File destination = new File(buildOutputLocation, "generated-snippets/some-operation"); destination.mkdirs(); FileSystemUtils.copyRecursively(new File("src/test/resources/some-operation"), destination); } @Test - public void codeBlockSnippetInclude() throws Exception { + void codeBlockSnippetInclude() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[snippets='curl-request']", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-simple")); } @Test - public void operationWithParameterizedName() throws Exception { + void operationWithParameterizedName() throws Exception { Attributes attributes = getAttributes(); attributes.setAttribute("name", "some"); this.options.setAttributes(attributes); @@ -95,20 +94,20 @@ public void operationWithParameterizedName() throws Exception { } @Test - public void codeBlockSnippetIncludeWithPdfBackend() throws Exception { + void codeBlockSnippetIncludeWithPdfBackend() throws Exception { File output = configurePdfOutput(); this.asciidoctor.convert("operation::some-operation[snippets='curl-request']", this.options); assertThat(extractStrings(output)).containsExactly("Curl request", "$ curl 'http://localhost:8080/' -i", "1"); } @Test - public void tableSnippetInclude() throws Exception { + void tableSnippetInclude() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[snippets='response-fields']", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-table")); } @Test - public void tableSnippetIncludeWithPdfBackend() throws Exception { + void tableSnippetIncludeWithPdfBackend() throws Exception { File output = configurePdfOutput(); this.asciidoctor.convert("operation::some-operation[snippets='response-fields']", this.options); assertThat(extractStrings(output)).containsExactly("Response fields", "Path", "Type", "Description", "a", @@ -116,14 +115,14 @@ public void tableSnippetIncludeWithPdfBackend() throws Exception { } @Test - public void includeSnippetInSection() throws Exception { + void includeSnippetInSection() throws Exception { String result = this.asciidoctor.convert("= A\n:doctype: book\n:sectnums:\n\nAlpha\n\n== B\n\nBravo\n\n" + "operation::some-operation[snippets='curl-request']\n\n== C\n", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-in-section")); } @Test - public void includeSnippetInSectionWithAbsoluteLevelOffset() throws Exception { + void includeSnippetInSectionWithAbsoluteLevelOffset() throws Exception { String result = this.asciidoctor .convert("= A\n:doctype: book\n:sectnums:\n:leveloffset: 1\n\nAlpha\n\n= B\n\nBravo\n\n" + "operation::some-operation[snippets='curl-request']\n\n= C\n", this.options); @@ -131,7 +130,7 @@ public void includeSnippetInSectionWithAbsoluteLevelOffset() throws Exception { } @Test - public void includeSnippetInSectionWithRelativeLevelOffset() throws Exception { + void includeSnippetInSectionWithRelativeLevelOffset() throws Exception { String result = this.asciidoctor .convert("= A\n:doctype: book\n:sectnums:\n:leveloffset: +1\n\nAlpha\n\n= B\n\nBravo\n\n" + "operation::some-operation[snippets='curl-request']\n\n= C\n", this.options); @@ -139,7 +138,7 @@ public void includeSnippetInSectionWithRelativeLevelOffset() throws Exception { } @Test - public void includeSnippetInSectionWithPdfBackend() throws Exception { + void includeSnippetInSectionWithPdfBackend() throws Exception { File output = configurePdfOutput(); this.asciidoctor.convert("== Section\n" + "operation::some-operation[snippets='curl-request']", this.options); assertThat(extractStrings(output)).containsExactly("Section", "Curl request", @@ -147,26 +146,26 @@ public void includeSnippetInSectionWithPdfBackend() throws Exception { } @Test - public void includeMultipleSnippets() throws Exception { + void includeMultipleSnippets() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[snippets='curl-request,http-request']", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("multiple-snippets")); } @Test - public void useMacroWithoutSnippetAttributeAddsAllSnippets() throws Exception { + void useMacroWithoutSnippetAttributeAddsAllSnippets() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[]", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("all-snippets")); } @Test - public void useMacroWithEmptySnippetAttributeAddsAllSnippets() throws Exception { + void useMacroWithEmptySnippetAttributeAddsAllSnippets() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[snippets=]", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("all-snippets")); } @Test - public void includingMissingSnippetAddsWarning() throws Exception { + void includingMissingSnippetAddsWarning() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[snippets='missing-snippet']", this.options); assertThat(result).startsWith(getExpectedContentFromFile("missing-snippet")); assertThat(CapturingLogHandler.getLogRecords()).hasSize(1); @@ -177,13 +176,13 @@ public void includingMissingSnippetAddsWarning() throws Exception { } @Test - public void defaultTitleIsProvidedForCustomSnippet() throws Exception { + void defaultTitleIsProvidedForCustomSnippet() throws Exception { String result = this.asciidoctor.convert("operation::some-operation[snippets='custom-snippet']", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("custom-snippet-default-title")); } @Test - public void missingOperationIsHandledGracefully() throws Exception { + void missingOperationIsHandledGracefully() throws Exception { String result = this.asciidoctor.convert("operation::missing-operation[]", this.options); assertThat(result).startsWith(getExpectedContentFromFile("missing-operation")); assertThat(CapturingLogHandler.getLogRecords()).hasSize(1); @@ -194,14 +193,14 @@ public void missingOperationIsHandledGracefully() throws Exception { } @Test - public void titleOfBuiltInSnippetCanBeCustomizedUsingDocumentAttribute() throws URISyntaxException, IOException { + void titleOfBuiltInSnippetCanBeCustomizedUsingDocumentAttribute() throws URISyntaxException, IOException { String result = this.asciidoctor.convert(":operation-curl-request-title: Example request\n" + "operation::some-operation[snippets='curl-request']", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("built-in-snippet-custom-title")); } @Test - public void titleOfCustomSnippetCanBeCustomizedUsingDocumentAttribute() throws Exception { + void titleOfCustomSnippetCanBeCustomizedUsingDocumentAttribute() throws Exception { String result = this.asciidoctor.convert(":operation-custom-snippet-title: Customized title\n" + "operation::some-operation[snippets='custom-snippet']", this.options); assertThat(result).isEqualTo(getExpectedContentFromFile("custom-snippet-custom-title")); diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java index 78ba919c..b5921dba 100644 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java +++ b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java @@ -21,7 +21,7 @@ import org.asciidoctor.Asciidoctor; import org.asciidoctor.Attributes; import org.asciidoctor.Options; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -30,23 +30,23 @@ * * @author Andy Wilkinson */ -public class DefaultAttributesPreprocessorTests { +class DefaultAttributesPreprocessorTests { @Test - public void snippetsAttributeIsSet() { + void snippetsAttributeIsSet() { String converted = createAsciidoctor().convert("{snippets}", createOptions("projectdir=../../..")); assertThat(converted).contains("build" + File.separatorChar + "generated-snippets"); } @Test - public void snippetsAttributeFromConvertArgumentIsNotOverridden() { + void snippetsAttributeFromConvertArgumentIsNotOverridden() { String converted = createAsciidoctor().convert("{snippets}", createOptions("snippets=custom projectdir=../../..")); assertThat(converted).contains("custom"); } @Test - public void snippetsAttributeFromDocumentPreambleIsNotOverridden() { + void snippetsAttributeFromDocumentPreambleIsNotOverridden() { String converted = createAsciidoctor().convert(":snippets: custom\n{snippets}", createOptions("projectdir=../../..")); assertThat(converted).contains("custom"); diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java index 8e8185b3..1ed21adb 100644 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java +++ b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,47 +19,42 @@ import java.io.File; import org.asciidoctor.Attributes; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.ValueSource; /** * Tests for Ruby operation block macro when used in a Gradle build. * * @author Andy Wilkinson */ -@RunWith(Parameterized.class) -public class GradleOperationBlockMacroTests extends AbstractOperationBlockMacroTests { +@ParameterizedClass +@ValueSource(strings = { "projectdir", "gradle-projectdir" }) +class GradleOperationBlockMacroTests extends AbstractOperationBlockMacroTests { private final String attributeName; - public GradleOperationBlockMacroTests(String attributeName) { + GradleOperationBlockMacroTests(String attributeName) { this.attributeName = attributeName; } - @Parameters(name = "{0}") - public static Object[] parameters() { - return new Object[] { "projectdir", "gradle-projectdir" }; - } - @Override protected Attributes getAttributes() { Attributes attributes = Attributes.builder() - .attribute(this.attributeName, new File(this.temp.getRoot(), "gradle-project").getAbsolutePath()) + .attribute(this.attributeName, new File(this.temp, "gradle-project").getAbsolutePath()) .build(); return attributes; } @Override protected File getBuildOutputLocation() { - File outputLocation = new File(this.temp.getRoot(), "gradle-project/build"); + File outputLocation = new File(this.temp, "gradle-project/build"); outputLocation.mkdirs(); return outputLocation; } @Override protected File getSourceLocation() { - File sourceLocation = new File(this.temp.getRoot(), "gradle-project/src/docs/asciidoc"); + File sourceLocation = new File(this.temp, "gradle-project/src/docs/asciidoc"); if (!sourceLocation.exists()) { sourceLocation.mkdirs(); } diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java index 702ed06b..f02f326f 100644 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java +++ b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2025 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. @@ -20,23 +20,23 @@ import java.io.IOException; import org.asciidoctor.Attributes; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; /** * Tests for Ruby operation block macro when used in a Maven build. * * @author Andy Wilkinson */ -public class MavenOperationBlockMacroTests extends AbstractOperationBlockMacroTests { +class MavenOperationBlockMacroTests extends AbstractOperationBlockMacroTests { - @Before - public void setMavenHome() { + @BeforeEach + void setMavenHome() { System.setProperty("maven.home", "maven-home"); } - @After - public void clearMavenHome() { + @AfterEach + void clearMavenHome() { System.clearProperty("maven.home"); } @@ -55,14 +55,14 @@ protected Attributes getAttributes() { @Override protected File getBuildOutputLocation() { - File outputLocation = new File(this.temp.getRoot(), "maven-project/target"); + File outputLocation = new File(this.temp, "maven-project/target"); outputLocation.mkdirs(); return outputLocation; } @Override protected File getSourceLocation() { - File sourceLocation = new File(this.temp.getRoot(), "maven-project/src/main/asciidoc"); + File sourceLocation = new File(this.temp, "maven-project/src/main/asciidoc"); if (!sourceLocation.exists()) { sourceLocation.mkdirs(); } diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java index 2bede597..9ca213b9 100644 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java +++ b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -21,9 +21,8 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; @@ -35,23 +34,23 @@ */ public class SnippetsDirectoryResolverTests { - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @TempDir + File temp; @Test public void mavenProjectsUseTargetGeneratedSnippets() throws IOException { - this.temporaryFolder.newFile("pom.xml"); + new File(this.temp, "pom.xml").createNewFile(); Map attributes = new HashMap<>(); - attributes.put("docdir", new File(this.temporaryFolder.getRoot(), "src/main/asciidoc").getAbsolutePath()); + attributes.put("docdir", new File(this.temp, "src/main/asciidoc").getAbsolutePath()); File snippetsDirectory = getMavenSnippetsDirectory(attributes); assertThat(snippetsDirectory).isAbsolute(); - assertThat(snippetsDirectory).isEqualTo(new File(this.temporaryFolder.getRoot(), "target/generated-snippets")); + assertThat(snippetsDirectory).isEqualTo(new File(this.temp, "target/generated-snippets")); } @Test public void illegalStateExceptionWhenMavenPomCannotBeFound() { Map attributes = new HashMap<>(); - String docdir = new File(this.temporaryFolder.getRoot(), "src/main/asciidoc").getAbsolutePath(); + String docdir = new File(this.temp, "src/main/asciidoc").getAbsolutePath(); attributes.put("docdir", docdir); assertThatIllegalStateException().isThrownBy(() -> getMavenSnippetsDirectory(attributes)) .withMessage("pom.xml not found in '" + docdir + "' or above"); diff --git a/spring-restdocs-core/build.gradle b/spring-restdocs-core/build.gradle index ded501e4..6832649b 100644 --- a/spring-restdocs-core/build.gradle +++ b/spring-restdocs-core/build.gradle @@ -32,6 +32,8 @@ task jmustacheRepackJar(type: Jar) { repackJar -> } dependencies { + compileOnly("org.apiguardian:apiguardian-api") + implementation("com.fasterxml.jackson.core:jackson-databind") implementation("org.springframework:spring-web") implementation(files(jmustacheRepackJar)) @@ -50,21 +52,29 @@ dependencies { optional("org.junit.jupiter:junit-jupiter-api") testFixturesApi(platform(project(":spring-restdocs-platform"))) - testFixturesApi("junit:junit") testFixturesApi("org.assertj:assertj-core") - testFixturesApi("org.hamcrest:hamcrest-core") + testFixturesApi("org.junit.jupiter:junit-jupiter") + testFixturesApi("org.mockito:mockito-core") + + testFixturesCompileOnly("org.apiguardian:apiguardian-api") + testFixturesImplementation(files(jmustacheRepackJar)) testFixturesImplementation("org.hamcrest:hamcrest-library") + testFixturesImplementation("org.mockito:mockito-core") testFixturesImplementation("org.springframework:spring-core") testFixturesImplementation("org.springframework:spring-web") + + testFixturesRuntimeOnly("org.junit.platform:junit-platform-launcher") + + testCompileOnly("org.apiguardian:apiguardian-api") - testImplementation("junit:junit") testImplementation("org.assertj:assertj-core") testImplementation("org.javamoney:moneta") testImplementation("org.mockito:mockito-core") testImplementation("org.springframework:spring-test") testRuntimeOnly("org.apache.tomcat.embed:tomcat-embed-el") + testRuntimeOnly("org.junit.platform:junit-platform-engine") } jar { @@ -81,3 +91,7 @@ components.java.withVariantsFromConfiguration(configurations.testFixturesApiElem components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } + +tasks.named("test") { + useJUnitPlatform() +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java index 95fbb3cc..fb89916b 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -74,9 +74,9 @@ public void document(Operation operation) throws IOException { RestDocumentationContext context = (RestDocumentationContext) operation.getAttributes() .get(RestDocumentationContext.class.getName()); WriterResolver writerResolver = (WriterResolver) operation.getAttributes().get(WriterResolver.class.getName()); + Map model = createModel(operation); + model.putAll(this.attributes); try (Writer writer = writerResolver.resolve(operation.getName(), this.snippetName, context)) { - Map model = createModel(operation); - model.putAll(this.attributes); TemplateEngine templateEngine = (TemplateEngine) operation.getAttributes() .get(TemplateEngine.class.getName()); writer.append(templateEngine.compileTemplate(this.templateName).render(model)); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java index de1a6543..bc0a5d09 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2025 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. @@ -16,25 +16,16 @@ package org.springframework.restdocs; -import java.util.Arrays; -import java.util.List; - -import org.junit.Rule; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpStatus; import org.springframework.restdocs.templates.TemplateFormat; import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.GeneratedSnippets; -import org.springframework.restdocs.testfixtures.OperationBuilder; import org.springframework.restdocs.testfixtures.SnippetConditions; import org.springframework.restdocs.testfixtures.SnippetConditions.CodeBlockCondition; import org.springframework.restdocs.testfixtures.SnippetConditions.HttpRequestCondition; import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition; import org.springframework.restdocs.testfixtures.SnippetConditions.TableCondition; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; import org.springframework.web.bind.annotation.RequestMethod; /** @@ -42,28 +33,11 @@ * * @author Andy Wilkinson */ -@RunWith(Parameterized.class) public abstract class AbstractSnippetTests { - protected final TemplateFormat templateFormat; - - @Rule - public GeneratedSnippets generatedSnippets; - - @Rule - public OperationBuilder operationBuilder; + protected final TemplateFormat templateFormat = TemplateFormats.asciidoctor(); - @Parameters(name = "{0}") - public static List parameters() { - return Arrays.asList(new Object[] { "Asciidoctor", TemplateFormats.asciidoctor() }, - new Object[] { "Markdown", TemplateFormats.markdown() }); - } - - protected AbstractSnippetTests(String name, TemplateFormat templateFormat) { - this.generatedSnippets = new GeneratedSnippets(templateFormat); - this.templateFormat = templateFormat; - this.operationBuilder = new OperationBuilder(this.templateFormat); - } + protected AssertableSnippets snippets; public CodeBlockCondition codeBlock(String language) { return this.codeBlock(language, null); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java index 733c0264..3b41392a 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mockito; @@ -55,7 +55,7 @@ * @author Andy Wilkinson * @author Filip Hrisafov */ -public class RestDocumentationGeneratorTests { +class RestDocumentationGeneratorTests { @SuppressWarnings("unchecked") private final RequestConverter requestConverter = mock(RequestConverter.class); @@ -80,7 +80,7 @@ public class RestDocumentationGeneratorTests { private final OperationPreprocessor responsePreprocessor = mock(OperationPreprocessor.class); @Test - public void basicHandling() throws IOException { + void basicHandling() throws IOException { given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); HashMap configuration = new HashMap<>(); @@ -90,7 +90,7 @@ public void basicHandling() throws IOException { } @Test - public void defaultSnippetsAreCalled() throws IOException { + void defaultSnippetsAreCalled() throws IOException { given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); HashMap configuration = new HashMap<>(); @@ -107,7 +107,7 @@ public void defaultSnippetsAreCalled() throws IOException { } @Test - public void defaultOperationRequestPreprocessorsAreCalled() throws IOException { + void defaultOperationRequestPreprocessorsAreCalled() throws IOException { given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); HashMap configuration = new HashMap<>(); @@ -128,7 +128,7 @@ public void defaultOperationRequestPreprocessorsAreCalled() throws IOException { } @Test - public void defaultOperationResponsePreprocessorsAreCalled() throws IOException { + void defaultOperationResponsePreprocessorsAreCalled() throws IOException { given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); HashMap configuration = new HashMap<>(); @@ -149,7 +149,7 @@ public void defaultOperationResponsePreprocessorsAreCalled() throws IOException } @Test - public void newGeneratorOnlyCallsItsSnippets() throws IOException { + void newGeneratorOnlyCallsItsSnippets() throws IOException { OperationRequestPreprocessor requestPreprocessor = mock(OperationRequestPreprocessor.class); OperationResponsePreprocessor responsePreprocessor = mock(OperationResponsePreprocessor.class); given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java index 54c7cd44..c69c879b 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -29,27 +29,27 @@ * @author Tomasz Kopczynski * @author Andy Wilkinson */ -public class ConcatenatingCommandFormatterTests { +class ConcatenatingCommandFormatterTests { private CommandFormatter singleLineFormat = new ConcatenatingCommandFormatter(" "); @Test - public void formattingAnEmptyListProducesAnEmptyString() { + void formattingAnEmptyListProducesAnEmptyString() { assertThat(this.singleLineFormat.format(Collections.emptyList())).isEqualTo(""); } @Test - public void formattingNullProducesAnEmptyString() { + void formattingNullProducesAnEmptyString() { assertThat(this.singleLineFormat.format(null)).isEqualTo(""); } @Test - public void formattingASingleElement() { + void formattingASingleElement() { assertThat(this.singleLineFormat.format(Collections.singletonList("alpha"))).isEqualTo(" alpha"); } @Test - public void formattingMultipleElements() { + void formattingMultipleElements() { assertThat(this.singleLineFormat.format(Arrays.asList("alpha", "bravo"))).isEqualTo(" alpha bravo"); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java index 2348adc4..e0136a1d 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,14 +19,11 @@ import java.io.IOException; import java.util.Base64; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; import static org.assertj.core.api.Assertions.assertThat; @@ -40,200 +37,209 @@ * @author Paul-Christian Volkmer * @author Tomasz Kopczynski */ -@RunWith(Parameterized.class) -public class CurlRequestSnippetTests extends AbstractSnippetTests { +class CurlRequestSnippetTests { private CommandFormatter commandFormatter = CliDocumentation.singleLineFormat(); - public CurlRequestSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @Test - public void getRequest() throws IOException { + @RenderedSnippetTest + void getRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X GET")); + .document(operationBuilder.request("http://localhost/foo").build()); + assertThat(snippets.curlRequest()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguage("bash").content("$ curl 'http://localhost/foo' -i -X GET")); } - @Test - public void nonGetRequest() throws IOException { + @RenderedSnippetTest + void nonGetRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("POST").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X POST")); + .document(operationBuilder.request("http://localhost/foo").method("POST").build()); + assertThat(snippets.curlRequest()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguage("bash").content("$ curl 'http://localhost/foo' -i -X POST")); } - @Test - public void requestWithContent() throws IOException { + @RenderedSnippetTest + void requestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").content("content").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X GET -d 'content'")); + .document(operationBuilder.request("http://localhost/foo").content("content").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X GET -d 'content'")); } - @Test - public void getRequestWithQueryString() throws IOException { + @RenderedSnippetTest + void getRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param=value").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo?param=value' -i -X GET")); + .document(operationBuilder.request("http://localhost/foo?param=value").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo?param=value' -i -X GET")); } - @Test - public void getRequestWithQueryStringWithNoValue() throws IOException { + @RenderedSnippetTest + void getRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo?param' -i -X GET")); + .document(operationBuilder.request("http://localhost/foo?param").build()); + assertThat(snippets.curlRequest()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguage("bash").content("$ curl 'http://localhost/foo?param' -i -X GET")); } - @Test - public void postRequestWithQueryString() throws IOException { + @RenderedSnippetTest + void postRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param=value").method("POST").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo?param=value' -i -X POST")); + .document(operationBuilder.request("http://localhost/foo?param=value").method("POST").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo?param=value' -i -X POST")); } - @Test - public void postRequestWithQueryStringWithNoValue() throws IOException { + @RenderedSnippetTest + void postRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param").method("POST").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo?param' -i -X POST")); + .document(operationBuilder.request("http://localhost/foo?param").method("POST").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo?param' -i -X POST")); } - @Test - public void postRequestWithOneParameter() throws IOException { + @RenderedSnippetTest + void postRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("POST").content("k1=v1").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); + .document(operationBuilder.request("http://localhost/foo").method("POST").content("k1=v1").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); } - @Test - public void postRequestWithOneParameterAndExplicitContentType() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithOneParameterAndExplicitContentType(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .method("POST") .content("k1=v1") .build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); } - @Test - public void postRequestWithOneParameterWithNoValue() throws IOException { + @RenderedSnippetTest + void postRequestWithOneParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("POST").content("k1=").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X POST -d 'k1='")); + .document(operationBuilder.request("http://localhost/foo").method("POST").content("k1=").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1='")); } - @Test - public void postRequestWithMultipleParameters() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("POST") .content("k1=v1&k1=v1-bis&k2=v2") .build()); - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash") - .withContent("$ curl 'http://localhost/foo' -i -X POST" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X POST" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); } - @Test - public void postRequestWithUrlEncodedParameter() throws IOException { + @RenderedSnippetTest + void postRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("POST").content("k1=a%26b").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'")); + .document(operationBuilder.request("http://localhost/foo").method("POST").content("k1=a%26b").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'")); } - @Test - public void postRequestWithJsonData() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithJsonData(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .content("{\"a\":\"alpha\"}") .build()); - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash").withContent( - "$ curl 'http://localhost/foo' -i -X POST -H 'Content-Type: application/json' -d '{\"a\":\"alpha\"}'")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content( + "$ curl 'http://localhost/foo' -i -X POST -H 'Content-Type: application/json' -d '{\"a\":\"alpha\"}'")); } - @Test - public void putRequestWithOneParameter() throws IOException { + @RenderedSnippetTest + void putRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("PUT").content("k1=v1").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'")); + .document(operationBuilder.request("http://localhost/foo").method("PUT").content("k1=v1").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'")); } - @Test - public void putRequestWithMultipleParameters() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void putRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("PUT") .content("k1=v1&k1=v1-bis&k2=v2") .build()); - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash") - .withContent("$ curl 'http://localhost/foo' -i -X PUT" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X PUT" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); } - @Test - public void putRequestWithUrlEncodedParameter() throws IOException { + @RenderedSnippetTest + void putRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("PUT").content("k1=a%26b").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'")); + .document(operationBuilder.request("http://localhost/foo").method("PUT").content("k1=a%26b").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'")); } - @Test - public void requestWithHeaders() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void requestWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash").withContent( - "$ curl 'http://localhost/foo' -i -X GET" + " -H 'Content-Type: application/json' -H 'a: alpha'")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X GET" + " -H 'Content-Type: application/json' -H 'a: alpha'")); } - @Test - public void requestWithHeadersMultiline() throws IOException { + @RenderedSnippetTest + void requestWithHeadersMultiline(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new CurlRequestSnippet(CliDocumentation.multiLineFormat()) - .document(this.operationBuilder.request("http://localhost/foo") + .document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent(String.format("$ curl 'http://localhost/foo' -i -X GET \\%n" + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content(String.format("$ curl 'http://localhost/foo' -i -X GET \\%n" + " -H 'Content-Type: application/json' \\%n" + " -H 'a: alpha'"))); } - @Test - public void requestWithCookies() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void requestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .cookie("name1", "value1") .cookie("name2", "value2") .build()); - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash") - .withContent("$ curl 'http://localhost/foo' -i -X GET" + " --cookie 'name1=value1;name2=value2'")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X GET" + " --cookie 'name1=value1;name2=value2'")); } - @Test - public void multipartPostWithNoSubmittedFileName() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPostWithNoSubmittedFileName(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("metadata", "{\"description\": \"foo\"}".getBytes()) .build()); String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H " + "'Content-Type: multipart/form-data' -F " + "'metadata={\"description\": \"foo\"}'"; - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash").withContent(expectedContent)); + assertThat(snippets.curlRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); } - @Test - public void multipartPostWithContentType() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPostWithContentType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", new byte[0]) @@ -242,12 +248,13 @@ public void multipartPostWithContentType() throws IOException { .build()); String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H " + "'Content-Type: multipart/form-data' -F " + "'image=@documents/images/example.png;type=image/png'"; - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash").withContent(expectedContent)); + assertThat(snippets.curlRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); } - @Test - public void multipartPost() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", new byte[0]) @@ -255,36 +262,38 @@ public void multipartPost() throws IOException { .build()); String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H " + "'Content-Type: multipart/form-data' -F " + "'image=@documents/images/example.png'"; - assertThat(this.generatedSnippets.curlRequest()).is(codeBlock("bash").withContent(expectedContent)); + assertThat(snippets.curlRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); } - @Test - public void basicAuthCredentialsAreSuppliedUsingUserOption() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void basicAuthCredentialsAreSuppliedUsingUserOption(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString("user:secret".getBytes())) .build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -u 'user:secret' -X GET")); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -u 'user:secret' -X GET")); } - @Test - public void customAttributes() throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void customAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.HOST, "api.example.com") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo' -i -X GET -H 'Host: api.example.com'" + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo' -i -X GET -H 'Host: api.example.com'" + " -H 'Content-Type: application/json' -H 'a: alpha'")); } - @Test - public void deleteWithQueryString() throws IOException { + @RenderedSnippetTest + void deleteWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new CurlRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); - assertThat(this.generatedSnippets.curlRequest()) - .is(codeBlock("bash").withContent("$ curl 'http://localhost/foo?a=alpha&b=bravo' -i " + "-X DELETE")); + .document(operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); + assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ curl 'http://localhost/foo?a=alpha&b=bravo' -i " + "-X DELETE")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java index 3d4f9ff8..89500ef0 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,14 +19,11 @@ import java.io.IOException; import java.util.Base64; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; import static org.assertj.core.api.Assertions.assertThat; @@ -41,249 +38,257 @@ * @author Raman Gupta * @author Tomasz Kopczynski */ -@RunWith(Parameterized.class) -public class HttpieRequestSnippetTests extends AbstractSnippetTests { +class HttpieRequestSnippetTests { private CommandFormatter commandFormatter = CliDocumentation.singleLineFormat(); - public HttpieRequestSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @Test - public void getRequest() throws IOException { + @RenderedSnippetTest + void getRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http GET 'http://localhost/foo'")); + .document(operationBuilder.request("http://localhost/foo").build()); + assertThat(snippets.httpieRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content("$ http GET 'http://localhost/foo'")); } - @Test - public void nonGetRequest() throws IOException { + @RenderedSnippetTest + void nonGetRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").method("POST").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http POST 'http://localhost/foo'")); + .document(operationBuilder.request("http://localhost/foo").method("POST").build()); + assertThat(snippets.httpieRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content("$ http POST 'http://localhost/foo'")); } - @Test - public void requestWithContent() throws IOException { + @RenderedSnippetTest + void requestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo").content("content").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ echo 'content' | http GET 'http://localhost/foo'")); + .document(operationBuilder.request("http://localhost/foo").content("content").build()); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ echo 'content' | http GET 'http://localhost/foo'")); } - @Test - public void getRequestWithQueryString() throws IOException { + @RenderedSnippetTest + void getRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param=value").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http GET 'http://localhost/foo?param=value'")); + .document(operationBuilder.request("http://localhost/foo?param=value").build()); + assertThat(snippets.httpieRequest()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguage("bash").content("$ http GET 'http://localhost/foo?param=value'")); } - @Test - public void getRequestWithQueryStringWithNoValue() throws IOException { + @RenderedSnippetTest + void getRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http GET 'http://localhost/foo?param'")); + .document(operationBuilder.request("http://localhost/foo?param").build()); + assertThat(snippets.httpieRequest()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguage("bash").content("$ http GET 'http://localhost/foo?param'")); } - @Test - public void postRequestWithQueryString() throws IOException { + @RenderedSnippetTest + void postRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param=value").method("POST").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http POST 'http://localhost/foo?param=value'")); + .document(operationBuilder.request("http://localhost/foo?param=value").method("POST").build()); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http POST 'http://localhost/foo?param=value'")); } - @Test - public void postRequestWithQueryStringWithNoValue() throws IOException { + @RenderedSnippetTest + void postRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?param").method("POST").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http POST 'http://localhost/foo?param'")); + .document(operationBuilder.request("http://localhost/foo?param").method("POST").build()); + assertThat(snippets.httpieRequest()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguage("bash").content("$ http POST 'http://localhost/foo?param'")); } - @Test - public void postRequestWithOneParameter() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1=v1") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --form POST 'http://localhost/foo' 'k1=v1'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form POST 'http://localhost/foo' 'k1=v1'")); } - @Test - public void postRequestWithOneParameterWithNoValue() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithOneParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --form POST 'http://localhost/foo' 'k1='")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form POST 'http://localhost/foo' 'k1='")); } - @Test - public void postRequestWithMultipleParameters() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1=v1&k1=v1-bis&k2=v2") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --form POST 'http://localhost/foo' 'k1=v1' 'k1=v1-bis' 'k2=v2'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form POST 'http://localhost/foo' 'k1=v1' 'k1=v1-bis' 'k2=v2'")); } - @Test - public void postRequestWithUrlEncodedParameter() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void postRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1=a%26b") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --form POST 'http://localhost/foo' 'k1=a&b'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form POST 'http://localhost/foo' 'k1=a&b'")); } - @Test - public void putRequestWithOneParameter() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void putRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("PUT") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1=v1") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --form PUT 'http://localhost/foo' 'k1=v1'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form PUT 'http://localhost/foo' 'k1=v1'")); } - @Test - public void putRequestWithMultipleParameters() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void putRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("PUT") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1=v1&k1=v1-bis&k2=v2") .build()); - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash") - .withContent("$ http --form PUT 'http://localhost/foo'" + " 'k1=v1' 'k1=v1-bis' 'k2=v2'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form PUT 'http://localhost/foo'" + " 'k1=v1' 'k1=v1-bis' 'k2=v2'")); } - @Test - public void putRequestWithUrlEncodedParameter() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void putRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .method("PUT") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .content("k1=a%26b") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --form PUT 'http://localhost/foo' 'k1=a&b'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --form PUT 'http://localhost/foo' 'k1=a&b'")); } - @Test - public void requestWithHeaders() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void requestWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash") - .withContent("$ http GET 'http://localhost/foo'" + " 'Content-Type:application/json' 'a:alpha'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content(("$ http GET 'http://localhost/foo'" + " 'Content-Type:application/json' 'a:alpha'"))); } - @Test - public void requestWithHeadersMultiline() throws IOException { + @RenderedSnippetTest + void requestWithHeadersMultiline(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new HttpieRequestSnippet(CliDocumentation.multiLineFormat()) - .document(this.operationBuilder.request("http://localhost/foo") + .document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash").withContent(String.format( - "$ http GET 'http://localhost/foo' \\%n" + " 'Content-Type:application/json' \\%n 'a:alpha'"))); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content(String.format("$ http GET 'http://localhost/foo' \\%n" + + " 'Content-Type:application/json' \\%n 'a:alpha'"))); } - @Test - public void requestWithCookies() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void requestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .cookie("name1", "value1") .cookie("name2", "value2") .build()); - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash") - .withContent("$ http GET 'http://localhost/foo'" + " 'Cookie:name1=value1' 'Cookie:name2=value2'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content(("$ http GET 'http://localhost/foo'" + " 'Cookie:name1=value1' 'Cookie:name2=value2'"))); } - @Test - public void multipartPostWithNoSubmittedFileName() throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("metadata", "{\"description\": \"foo\"}".getBytes()) - .build()); + @RenderedSnippetTest + void multipartPostWithNoSubmittedFileName(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") + .method("POST") + .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) + .part("metadata", "{\"description\": \"foo\"}".getBytes()) + .build()); String expectedContent = "$ http --multipart POST 'http://localhost/upload'" + " 'metadata'='{\"description\": \"foo\"}'"; - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash").withContent(expectedContent)); + assertThat(snippets.httpieRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); } - @Test - public void multipartPostWithContentType() throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", new byte[0]) - .header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE) - .submittedFileName("documents/images/example.png") - .build()); + @RenderedSnippetTest + void multipartPostWithContentType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") + .method("POST") + .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) + .part("image", new byte[0]) + .header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE) + .submittedFileName("documents/images/example.png") + .build()); // httpie does not yet support manually set content type by part String expectedContent = "$ http --multipart POST 'http://localhost/upload'" + " 'image'@'documents/images/example.png'"; - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash").withContent(expectedContent)); + assertThat(snippets.httpieRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); } - @Test - public void multipartPost() throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", new byte[0]) - .submittedFileName("documents/images/example.png") - .build()); + @RenderedSnippetTest + void multipartPost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") + .method("POST") + .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) + .part("image", new byte[0]) + .submittedFileName("documents/images/example.png") + .build()); String expectedContent = "$ http --multipart POST 'http://localhost/upload'" + " 'image'@'documents/images/example.png'"; - assertThat(this.generatedSnippets.httpieRequest()).is(codeBlock("bash").withContent(expectedContent)); + assertThat(snippets.httpieRequest()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); } - @Test - public void basicAuthCredentialsAreSuppliedUsingAuthOption() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void basicAuthCredentialsAreSuppliedUsingAuthOption(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString("user:secret".getBytes())) .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http --auth 'user:secret' GET 'http://localhost/foo'")); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http --auth 'user:secret' GET 'http://localhost/foo'")); } - @Test - public void customAttributes() throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void customAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") .header(HttpHeaders.HOST, "api.example.com") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http GET 'http://localhost/foo' 'Host:api.example.com'" + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http GET 'http://localhost/foo' 'Host:api.example.com'" + " 'Content-Type:application/json' 'a:alpha'")); } - @Test - public void deleteWithQueryString() throws IOException { + @RenderedSnippetTest + void deleteWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpieRequestSnippet(this.commandFormatter) - .document(this.operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); - assertThat(this.generatedSnippets.httpieRequest()) - .is(codeBlock("bash").withContent("$ http DELETE 'http://localhost/foo?a=alpha&b=bravo'")); + .document(operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); + assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") + .content("$ http DELETE 'http://localhost/foo?a=alpha&b=bravo'")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java index 07fae9e5..aa6c6814 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -63,13 +63,13 @@ * @author Andy Wilkinson * @author Filip Hrisafov */ -public class RestDocumentationConfigurerTests { +class RestDocumentationConfigurerTests { private final TestRestDocumentationConfigurer configurer = new TestRestDocumentationConfigurer(); @SuppressWarnings("unchecked") @Test - public void defaultConfiguration() { + void defaultConfiguration() { Map configuration = new HashMap<>(); this.configurer.apply(configuration, createContext()); assertThat(configuration).containsKey(TemplateEngine.class.getName()); @@ -102,7 +102,7 @@ public void defaultConfiguration() { } @Test - public void customTemplateEngine() { + void customTemplateEngine() { Map configuration = new HashMap<>(); TemplateEngine templateEngine = mock(TemplateEngine.class); this.configurer.templateEngine(templateEngine).apply(configuration, createContext()); @@ -110,7 +110,7 @@ public void customTemplateEngine() { } @Test - public void customWriterResolver() { + void customWriterResolver() { Map configuration = new HashMap<>(); WriterResolver writerResolver = mock(WriterResolver.class); this.configurer.writerResolver(writerResolver).apply(configuration, createContext()); @@ -118,7 +118,7 @@ public void customWriterResolver() { } @Test - public void customDefaultSnippets() { + void customDefaultSnippets() { Map configuration = new HashMap<>(); this.configurer.snippets().withDefaults(CliDocumentation.curlRequest()).apply(configuration, createContext()); assertThat(configuration).containsKey(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); @@ -133,7 +133,7 @@ public void customDefaultSnippets() { @SuppressWarnings("unchecked") @Test - public void additionalDefaultSnippets() { + void additionalDefaultSnippets() { Map configuration = new HashMap<>(); Snippet snippet = mock(Snippet.class); this.configurer.snippets().withAdditionalDefaults(snippet).apply(configuration, createContext()); @@ -148,7 +148,7 @@ public void additionalDefaultSnippets() { } @Test - public void customSnippetEncoding() { + void customSnippetEncoding() { Map configuration = new HashMap<>(); this.configurer.snippets().withEncoding("ISO-8859-1"); this.configurer.apply(configuration, createContext()); @@ -162,7 +162,7 @@ public void customSnippetEncoding() { } @Test - public void customTemplateFormat() { + void customTemplateFormat() { Map configuration = new HashMap<>(); this.configurer.snippets().withTemplateFormat(TemplateFormats.markdown()).apply(configuration, createContext()); assertThat(configuration).containsKey(SnippetConfiguration.class.getName()); @@ -174,7 +174,7 @@ public void customTemplateFormat() { @SuppressWarnings("unchecked") @Test - public void asciidoctorTableCellContentLambaIsInstalledWhenUsingAsciidoctorTemplateFormat() { + void asciidoctorTableCellContentLambaIsInstalledWhenUsingAsciidoctorTemplateFormat() { Map configuration = new HashMap<>(); this.configurer.apply(configuration, createContext()); TemplateEngine templateEngine = (TemplateEngine) configuration.get(TemplateEngine.class.getName()); @@ -187,7 +187,7 @@ public void asciidoctorTableCellContentLambaIsInstalledWhenUsingAsciidoctorTempl @SuppressWarnings("unchecked") @Test - public void asciidoctorTableCellContentLambaIsNotInstalledWhenUsingNonAsciidoctorTemplateFormat() { + void asciidoctorTableCellContentLambaIsNotInstalledWhenUsingNonAsciidoctorTemplateFormat() { Map configuration = new HashMap<>(); this.configurer.snippetConfigurer.withTemplateFormat(TemplateFormats.markdown()); this.configurer.apply(configuration, createContext()); @@ -199,7 +199,7 @@ public void asciidoctorTableCellContentLambaIsNotInstalledWhenUsingNonAsciidocto } @Test - public void customDefaultOperationRequestPreprocessor() { + void customDefaultOperationRequestPreprocessor() { Map configuration = new HashMap<>(); this.configurer.operationPreprocessors() .withRequestDefaults(Preprocessors.prettyPrint(), Preprocessors.modifyHeaders().remove("Foo")) @@ -214,7 +214,7 @@ public void customDefaultOperationRequestPreprocessor() { } @Test - public void customDefaultOperationResponsePreprocessor() { + void customDefaultOperationResponsePreprocessor() { Map configuration = new HashMap<>(); this.configurer.operationPreprocessors() .withResponseDefaults(Preprocessors.prettyPrint(), Preprocessors.modifyHeaders().remove("Foo")) diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java index 243dcf00..c5425c38 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -30,7 +30,7 @@ * * @author Andy Wilkinson */ -public class ConstraintDescriptionsTests { +class ConstraintDescriptionsTests { private final ConstraintResolver constraintResolver = mock(ConstraintResolver.class); @@ -41,7 +41,7 @@ public class ConstraintDescriptionsTests { this.constraintResolver, this.constraintDescriptionResolver); @Test - public void descriptionsForConstraints() { + void descriptionsForConstraints() { Constraint constraint1 = new Constraint("constraint1", Collections.emptyMap()); Constraint constraint2 = new Constraint("constraint2", Collections.emptyMap()); given(this.constraintResolver.resolveForProperty("foo", Constrained.class)) @@ -52,7 +52,7 @@ public void descriptionsForConstraints() { } @Test - public void emptyListOfDescriptionsWhenThereAreNoConstraints() { + void emptyListOfDescriptionsWhenThereAreNoConstraints() { given(this.constraintResolver.resolveForProperty("foo", Constrained.class)) .willReturn(Collections.emptyList()); assertThat(this.constraintDescriptions.descriptionsForProperty("foo").size()).isEqualTo(0); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java index f4b5e2af..7a977a9a 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java @@ -60,7 +60,7 @@ import org.hibernate.validator.constraints.Mod10Check; import org.hibernate.validator.constraints.Mod11Check; import org.hibernate.validator.constraints.Range; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.ClassPathResource; @@ -77,183 +77,183 @@ * * @author Andy Wilkinson */ -public class ResourceBundleConstraintDescriptionResolverTests { +class ResourceBundleConstraintDescriptionResolverTests { private final ResourceBundleConstraintDescriptionResolver resolver = new ResourceBundleConstraintDescriptionResolver(); @Test - public void defaultMessageAssertFalse() { + void defaultMessageAssertFalse() { assertThat(constraintDescriptionForField("assertFalse")).isEqualTo("Must be false"); } @Test - public void defaultMessageAssertTrue() { + void defaultMessageAssertTrue() { assertThat(constraintDescriptionForField("assertTrue")).isEqualTo("Must be true"); } @Test - public void defaultMessageCodePointLength() { + void defaultMessageCodePointLength() { assertThat(constraintDescriptionForField("codePointLength")) .isEqualTo("Code point length must be between 2 and 5 inclusive"); } @Test - public void defaultMessageCurrency() { + void defaultMessageCurrency() { assertThat(constraintDescriptionForField("currency")) .isEqualTo("Must be in an accepted currency unit (GBP, USD)"); } @Test - public void defaultMessageDecimalMax() { + void defaultMessageDecimalMax() { assertThat(constraintDescriptionForField("decimalMax")).isEqualTo("Must be at most 9.875"); } @Test - public void defaultMessageDecimalMin() { + void defaultMessageDecimalMin() { assertThat(constraintDescriptionForField("decimalMin")).isEqualTo("Must be at least 1.5"); } @Test - public void defaultMessageDigits() { + void defaultMessageDigits() { assertThat(constraintDescriptionForField("digits")) .isEqualTo("Must have at most 2 integral digits and 5 fractional digits"); } @Test - public void defaultMessageFuture() { + void defaultMessageFuture() { assertThat(constraintDescriptionForField("future")).isEqualTo("Must be in the future"); } @Test - public void defaultMessageFutureOrPresent() { + void defaultMessageFutureOrPresent() { assertThat(constraintDescriptionForField("futureOrPresent")).isEqualTo("Must be in the future or the present"); } @Test - public void defaultMessageMax() { + void defaultMessageMax() { assertThat(constraintDescriptionForField("max")).isEqualTo("Must be at most 10"); } @Test - public void defaultMessageMin() { + void defaultMessageMin() { assertThat(constraintDescriptionForField("min")).isEqualTo("Must be at least 10"); } @Test - public void defaultMessageNotNull() { + void defaultMessageNotNull() { assertThat(constraintDescriptionForField("notNull")).isEqualTo("Must not be null"); } @Test - public void defaultMessageNull() { + void defaultMessageNull() { assertThat(constraintDescriptionForField("nul")).isEqualTo("Must be null"); } @Test - public void defaultMessagePast() { + void defaultMessagePast() { assertThat(constraintDescriptionForField("past")).isEqualTo("Must be in the past"); } @Test - public void defaultMessagePastOrPresent() { + void defaultMessagePastOrPresent() { assertThat(constraintDescriptionForField("pastOrPresent")).isEqualTo("Must be in the past or the present"); } @Test - public void defaultMessagePattern() { + void defaultMessagePattern() { assertThat(constraintDescriptionForField("pattern")) .isEqualTo("Must match the regular expression `[A-Z][a-z]+`"); } @Test - public void defaultMessageSize() { + void defaultMessageSize() { assertThat(constraintDescriptionForField("size")).isEqualTo("Size must be between 2 and 10 inclusive"); } @Test - public void defaultMessageCreditCardNumber() { + void defaultMessageCreditCardNumber() { assertThat(constraintDescriptionForField("creditCardNumber")) .isEqualTo("Must be a well-formed credit card number"); } @Test - public void defaultMessageEan() { + void defaultMessageEan() { assertThat(constraintDescriptionForField("ean")).isEqualTo("Must be a well-formed EAN13 number"); } @Test - public void defaultMessageEmail() { + void defaultMessageEmail() { assertThat(constraintDescriptionForField("email")).isEqualTo("Must be a well-formed email address"); } @Test - public void defaultMessageLength() { + void defaultMessageLength() { assertThat(constraintDescriptionForField("length")).isEqualTo("Length must be between 2 and 10 inclusive"); } @Test - public void defaultMessageLuhnCheck() { + void defaultMessageLuhnCheck() { assertThat(constraintDescriptionForField("luhnCheck")) .isEqualTo("Must pass the Luhn Modulo 10 checksum algorithm"); } @Test - public void defaultMessageMod10Check() { + void defaultMessageMod10Check() { assertThat(constraintDescriptionForField("mod10Check")).isEqualTo("Must pass the Mod10 checksum algorithm"); } @Test - public void defaultMessageMod11Check() { + void defaultMessageMod11Check() { assertThat(constraintDescriptionForField("mod11Check")).isEqualTo("Must pass the Mod11 checksum algorithm"); } @Test - public void defaultMessageNegative() { + void defaultMessageNegative() { assertThat(constraintDescriptionForField("negative")).isEqualTo("Must be negative"); } @Test - public void defaultMessageNegativeOrZero() { + void defaultMessageNegativeOrZero() { assertThat(constraintDescriptionForField("negativeOrZero")).isEqualTo("Must be negative or zero"); } @Test - public void defaultMessageNotBlank() { + void defaultMessageNotBlank() { assertThat(constraintDescriptionForField("notBlank")).isEqualTo("Must not be blank"); } @Test - public void defaultMessageNotEmpty() { + void defaultMessageNotEmpty() { assertThat(constraintDescriptionForField("notEmpty")).isEqualTo("Must not be empty"); } @Test - public void defaultMessageNotEmptyHibernateValidator() { + void defaultMessageNotEmptyHibernateValidator() { assertThat(constraintDescriptionForField("notEmpty")).isEqualTo("Must not be empty"); } @Test - public void defaultMessagePositive() { + void defaultMessagePositive() { assertThat(constraintDescriptionForField("positive")).isEqualTo("Must be positive"); } @Test - public void defaultMessagePositiveOrZero() { + void defaultMessagePositiveOrZero() { assertThat(constraintDescriptionForField("positiveOrZero")).isEqualTo("Must be positive or zero"); } @Test - public void defaultMessageRange() { + void defaultMessageRange() { assertThat(constraintDescriptionForField("range")).isEqualTo("Must be at least 10 and at most 100"); } @Test - public void defaultMessageUrl() { + void defaultMessageUrl() { assertThat(constraintDescriptionForField("url")).isEqualTo("Must be a well-formed URL"); } @Test - public void customMessage() { + void customMessage() { Thread.currentThread().setContextClassLoader(new ClassLoader() { @Override @@ -279,7 +279,7 @@ public URL getResource(String name) { } @Test - public void customResourceBundle() { + void customResourceBundle() { ResourceBundle bundle = new ListResourceBundle() { @Override @@ -294,7 +294,7 @@ protected Object[][] getContents() { } @Test - public void allBeanValidationConstraintsAreTested() throws Exception { + void allBeanValidationConstraintsAreTested() throws Exception { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources("jakarta/validation/constraints/*.class"); Set> beanValidationConstraints = new HashSet<>(); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java index 3d900b3c..203817f8 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -35,7 +35,7 @@ import org.assertj.core.description.TextDescription; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -44,19 +44,19 @@ * * @author Andy Wilkinson */ -public class ValidatorConstraintResolverTests { +class ValidatorConstraintResolverTests { private final ValidatorConstraintResolver resolver = new ValidatorConstraintResolver(); @Test - public void singleFieldConstraint() { + void singleFieldConstraint() { List constraints = this.resolver.resolveForProperty("single", ConstrainedFields.class); assertThat(constraints).hasSize(1); assertThat(constraints.get(0).getName()).isEqualTo(NotNull.class.getName()); } @Test - public void multipleFieldConstraints() { + void multipleFieldConstraints() { List constraints = this.resolver.resolveForProperty("multiple", ConstrainedFields.class); assertThat(constraints).hasSize(2); assertThat(constraints.get(0)).is(constraint(NotNull.class)); @@ -64,13 +64,13 @@ public void multipleFieldConstraints() { } @Test - public void noFieldConstraints() { + void noFieldConstraints() { List constraints = this.resolver.resolveForProperty("none", ConstrainedFields.class); assertThat(constraints).hasSize(0); } @Test - public void compositeConstraint() { + void compositeConstraint() { List constraints = this.resolver.resolveForProperty("composite", ConstrainedFields.class); assertThat(constraints).hasSize(1); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetFailureTests.java deleted file mode 100644 index 2dfd9573..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetFailureTests.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for failures when rendering {@link RequestCookiesSnippet} due to missing or - * undocumented cookies. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - */ -public class RequestCookiesSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void missingRequestCookie() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestCookiesSnippet( - Collections.singletonList(CookieDocumentation.cookieWithName("JSESSIONID").description("one"))) - .document(this.operationBuilder.request("http://localhost/").build())) - .withMessage("Cookies with the following names were not found in the request: [JSESSIONID]"); - } - - @Test - public void undocumentedRequestCookie() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestCookiesSnippet(Collections.emptyList()).document( - this.operationBuilder.request("http://localhost/").cookie("JSESSIONID", "1234abcd5678efgh").build())) - .withMessageEndingWith("Cookies with the following names were not documented: [JSESSIONID]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java index b40198e2..febac6e0 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,18 +20,15 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -42,138 +39,141 @@ * @author Clyde Stubbs * @author Andy Wilkinson */ -public class RequestCookiesSnippetTests extends AbstractSnippetTests { - - public RequestCookiesSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class RequestCookiesSnippetTests { - @Test - public void requestWithCookies() throws IOException { + @RenderedSnippetTest + void requestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestCookiesSnippet( Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .cookie("tz", "Europe%2FLondon") .cookie("logged_in", "true") .build()); - assertThat(this.generatedSnippets.requestCookies()) - .is(tableWithHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); + assertThat(snippets.requestCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); } - @Test - public void ignoredRequestCookie() throws IOException { + @RenderedSnippetTest + void ignoredRequestCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestCookiesSnippet( Arrays.asList(cookieWithName("tz").ignored(), cookieWithName("logged_in").description("two"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .cookie("tz", "Europe%2FLondon") .cookie("logged_in", "true") .build()); - assertThat(this.generatedSnippets.requestCookies()) - .is(tableWithHeader("Name", "Description").row("`logged_in`", "two")); + assertThat(snippets.requestCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`logged_in`", "two")); } - @Test - public void allUndocumentedCookiesCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedCookiesCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestCookiesSnippet( Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")), true) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .cookie("tz", "Europe%2FLondon") .cookie("logged_in", "true") .cookie("user_session", "abcd1234efgh5678") .build()); - assertThat(this.generatedSnippets.requestCookies()) - .is(tableWithHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); + assertThat(snippets.requestCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); } - @Test - public void missingOptionalCookie() throws IOException { + @RenderedSnippetTest + void missingOptionalCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestCookiesSnippet(Arrays.asList(cookieWithName("tz").description("one").optional(), cookieWithName("logged_in").description("two"))) - .document(this.operationBuilder.request("http://localhost/").cookie("logged_in", "true").build()); - assertThat(this.generatedSnippets.requestCookies()) - .is(tableWithHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); + .document(operationBuilder.request("http://localhost/").cookie("logged_in", "true").build()); + assertThat(snippets.requestCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); } - @Test - public void requestCookiesWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-cookies")) - .willReturn(snippetResource("request-cookies-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-cookies", template = "request-cookies-with-title") + void requestCookiesWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestCookiesSnippet(Collections.singletonList(cookieWithName("tz").description("one")), attributes(key("title").value("Custom title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .cookie("tz", "Europe%2FLondon") - .build()); - assertThat(this.generatedSnippets.requestCookies()).contains("Custom title"); + .document(operationBuilder.request("http://localhost/").cookie("tz", "Europe%2FLondon").build()); + assertThat(snippets.requestCookies()).contains("Custom title"); } - @Test - public void requestCookiesWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-cookies")) - .willReturn(snippetResource("request-cookies-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-cookies", template = "request-cookies-with-extra-column") + void requestCookiesWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestCookiesSnippet( Arrays.asList(cookieWithName("tz").description("one").attributes(key("foo").value("alpha")), cookieWithName("logged_in").description("two").attributes(key("foo").value("bravo")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .cookie("tz", "Europe%2FLondon") .cookie("logged_in", "true") .build()); - assertThat(this.generatedSnippets.requestCookies()).is(// - tableWithHeader("Name", "Description", "Foo").row("tz", "one", "alpha") - .row("logged_in", "two", "bravo")); + assertThat(snippets.requestCookies()).isTable((table) -> table.withHeader("Name", "Description", "Foo") + .row("tz", "one", "alpha") + .row("logged_in", "two", "bravo")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestCookiesSnippet( Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two"))) .and(cookieWithName("user_session").description("three")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .cookie("tz", "Europe%2FLondon") .cookie("logged_in", "true") .cookie("user_session", "abcd1234efgh5678") .build()); - assertThat(this.generatedSnippets.requestCookies()).is(tableWithHeader("Name", "Description").row("`tz`", "one") + assertThat(snippets.requestCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`tz`", "one") .row("`logged_in`", "two") .row("`user_session`", "three")); } - @Test - public void additionalDescriptorsWithRelaxedRequestCookies() throws IOException { + @RenderedSnippetTest + void additionalDescriptorsWithRelaxedRequestCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestCookiesSnippet( Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")), true) .and(cookieWithName("user_session").description("three")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .cookie("tz", "Europe%2FLondon") .cookie("logged_in", "true") .cookie("user_session", "abcd1234efgh5678") .cookie("color_theme", "light") .build()); - assertThat(this.generatedSnippets.requestCookies()).is(tableWithHeader("Name", "Description").row("`tz`", "one") + assertThat(snippets.requestCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`tz`", "one") .row("`logged_in`", "two") .row("`user_session`", "three")); } - @Test - public void tableCellContentIsEscapedWhenNecessary() throws IOException { + @RenderedSnippetTest + void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestCookiesSnippet(Collections.singletonList(cookieWithName("Foo|Bar").description("one|two"))) - .document(this.operationBuilder.request("http://localhost/").cookie("Foo|Bar", "baz").build()); - assertThat(this.generatedSnippets.requestCookies()).is(tableWithHeader("Name", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.request("http://localhost/").cookie("Foo|Bar", "baz").build()); + assertThat(snippets.requestCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); + } + + @SnippetTest + void missingRequestCookie(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestCookiesSnippet( + Collections.singletonList(CookieDocumentation.cookieWithName("JSESSIONID").description("one"))) + .document(operationBuilder.request("http://localhost/").build())) + .withMessage("Cookies with the following names were not found in the request: [JSESSIONID]"); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void undocumentedRequestCookie(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestCookiesSnippet(Collections.emptyList()).document( + operationBuilder.request("http://localhost/").cookie("JSESSIONID", "1234abcd5678efgh").build())) + .withMessageEndingWith("Cookies with the following names were not documented: [JSESSIONID]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetFailureTests.java deleted file mode 100644 index e1249a85..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetFailureTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; - -/** - * Tests for failures when rendering {@link ResponseCookiesSnippet} due to missing or - * undocumented cookies. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - */ -public class ResponseCookiesSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void missingResponseCookie() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseCookiesSnippet( - Collections.singletonList(cookieWithName("JSESSIONID").description("one"))) - .document(this.operationBuilder.response().build())) - .withMessage("Cookies with the following names were not found in the response: [JSESSIONID]"); - } - - @Test - public void undocumentedResponseCookie() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseCookiesSnippet(Collections.emptyList()) - .document(this.operationBuilder.response().cookie("JSESSIONID", "1234abcd5678efgh").build())) - .withMessageEndingWith("Cookies with the following names were not documented: [JSESSIONID]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java index ad052243..2d7883e4 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,18 +20,12 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -42,141 +36,127 @@ * @author Clyde Stubbs * @author Andy Wilkinson */ -public class ResponseCookiesSnippetTests extends AbstractSnippetTests { - - public ResponseCookiesSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class ResponseCookiesSnippetTests { - @Test - public void responseWithCookies() throws IOException { + @RenderedSnippetTest + void responseWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one"), cookieWithName("user_session").description("two"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .cookie("has_recent_activity", "true") .cookie("user_session", "1234abcd5678efgh") .build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description").row("`has_recent_activity`", "one") - .row("`user_session`", "two")); + assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`has_recent_activity`", "one") + .row("`user_session`", "two")); } - @Test - public void ignoredResponseCookie() throws IOException { + @RenderedSnippetTest + void ignoredResponseCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").ignored(), cookieWithName("user_session").description("two"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .cookie("has_recent_activity", "true") .cookie("user_session", "1234abcd5678efgh") .build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description").row("`user_session`", "two")); + assertThat(snippets.responseCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`user_session`", "two")); } - @Test - public void allUndocumentedResponseCookiesCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedResponseCookiesCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one"), cookieWithName("user_session").description("two")), true) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .cookie("has_recent_activity", "true") .cookie("user_session", "1234abcd5678efgh") .cookie("some_cookie", "value") .build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description").row("`has_recent_activity`", "one") - .row("`user_session`", "two")); + assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`has_recent_activity`", "one") + .row("`user_session`", "two")); } - @Test - public void missingOptionalResponseCookie() throws IOException { + @RenderedSnippetTest + void missingOptionalResponseCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one").optional(), cookieWithName("user_session").description("two"))) - .document(this.operationBuilder.response().cookie("user_session", "1234abcd5678efgh").build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description").row("`has_recent_activity`", "one") - .row("`user_session`", "two")); + .document(operationBuilder.response().cookie("user_session", "1234abcd5678efgh").build()); + assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`has_recent_activity`", "one") + .row("`user_session`", "two")); } - @Test - public void responseCookiesWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-cookies")) - .willReturn(snippetResource("response-cookies-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-cookies", template = "response-cookies-with-title") + void responseCookiesWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseCookiesSnippet(Collections.singletonList(cookieWithName("has_recent_activity").description("one")), attributes(key("title").value("Custom title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() - .cookie("has_recent_activity", "true") - .build()); - assertThat(this.generatedSnippets.responseCookies()).contains("Custom title"); + .document(operationBuilder.response().cookie("has_recent_activity", "true").build()); + assertThat(snippets.responseCookies()).contains("Custom title"); } - @Test - public void responseCookiesWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-cookies")) - .willReturn(snippetResource("response-cookies-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-cookies", template = "response-cookies-with-extra-column") + void responseCookiesWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseCookiesSnippet(Arrays.asList( cookieWithName("has_recent_activity").description("one").attributes(key("foo").value("alpha")), cookieWithName("user_session").description("two").attributes(key("foo").value("bravo")), cookieWithName("color_theme").description("three").attributes(key("foo").value("charlie")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() + .document(operationBuilder.response() .cookie("has_recent_activity", "true") .cookie("user_session", "1234abcd5678efgh") .cookie("color_theme", "high_contrast") .build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description", "Foo").row("has_recent_activity", "one", "alpha") - .row("user_session", "two", "bravo") - .row("color_theme", "three", "charlie")); + assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description", "Foo") + .row("has_recent_activity", "one", "alpha") + .row("user_session", "two", "bravo") + .row("color_theme", "three", "charlie")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { CookieDocumentation .responseCookies(cookieWithName("has_recent_activity").description("one"), cookieWithName("user_session").description("two")) .and(cookieWithName("color_theme").description("three")) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .cookie("has_recent_activity", "true") .cookie("user_session", "1234abcd5678efgh") .cookie("color_theme", "light") .build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description").row("`has_recent_activity`", "one") - .row("`user_session`", "two") - .row("`color_theme`", "three")); + assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`has_recent_activity`", "one") + .row("`user_session`", "two") + .row("`color_theme`", "three")); } - @Test - public void additionalDescriptorsWithRelaxedResponseCookies() throws IOException { + @RenderedSnippetTest + void additionalDescriptorsWithRelaxedResponseCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { CookieDocumentation.relaxedResponseCookies(cookieWithName("has_recent_activity").description("one")) .and(cookieWithName("color_theme").description("two")) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .cookie("has_recent_activity", "true") .cookie("user_session", "1234abcd5678efgh") .cookie("color_theme", "light") .build()); - assertThat(this.generatedSnippets.responseCookies()) - .is(tableWithHeader("Name", "Description").row("`has_recent_activity`", "one").row("`color_theme`", "two")); + assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") + .row("`has_recent_activity`", "one") + .row("`color_theme`", "two")); } - @Test - public void tableCellContentIsEscapedWhenNecessary() throws IOException { + @RenderedSnippetTest + void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseCookiesSnippet(Collections.singletonList(cookieWithName("Foo|Bar").description("one|two"))) - .document(this.operationBuilder.response().cookie("Foo|Bar", "baz").build()); - assertThat(this.generatedSnippets.responseCookies()).is(tableWithHeader("Name", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); - } - - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + .document(operationBuilder.response().cookie("Foo|Bar", "baz").build()); + assertThat(snippets.responseCookies()) + .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java deleted file mode 100644 index a980632c..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetFailureTests.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.util.Arrays; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; - -/** - * Tests for failures when rendering {@link RequestHeadersSnippet} due to missing or - * undocumented headers. - * - * @author Andy Wilkinson - */ -public class RequestHeadersSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void missingRequestHeader() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description("one"))) - .document(this.operationBuilder.request("http://localhost/").build())) - .withMessage("Headers with the following names were not found in the request: [Accept]"); - } - - @Test - public void undocumentedRequestHeaderAndMissingRequestHeader() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description("one"))) - .document(this.operationBuilder.request("http://localhost/").header("X-Test", "test").build())) - .withMessageEndingWith("Headers with the following names were not found in the request: [Accept]"); - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java index edb90cb5..51f16ccc 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,18 +19,15 @@ import java.io.IOException; import java.util.Arrays; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -41,19 +38,15 @@ * @author Andreas Evers * @author Andy Wilkinson */ -public class RequestHeadersSnippetTests extends AbstractSnippetTests { - - public RequestHeadersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class RequestHeadersSnippetTests { - @Test - public void requestWithHeaders() throws IOException { + @RenderedSnippetTest + void requestWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"), headerWithName("Accept").description("two"), headerWithName("Accept-Encoding").description("three"), headerWithName("Accept-Language").description("four"), headerWithName("Cache-Control").description("five"), headerWithName("Connection").description("six"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .header("X-Test", "test") .header("Accept", "*/*") .header("Accept-Encoding", "gzip, deflate") @@ -61,79 +54,69 @@ public void requestWithHeaders() throws IOException { .header("Cache-Control", "max-age=0") .header("Connection", "keep-alive") .build()); - assertThat(this.generatedSnippets.requestHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one") - .row("`Accept`", "two") - .row("`Accept-Encoding`", "three") - .row("`Accept-Language`", "four") - .row("`Cache-Control`", "five") - .row("`Connection`", "six")); + assertThat(snippets.requestHeaders()).isTable((table) -> table.withHeader("Name", "Description") + .row("`X-Test`", "one") + .row("`Accept`", "two") + .row("`Accept-Encoding`", "three") + .row("`Accept-Language`", "four") + .row("`Cache-Control`", "five") + .row("`Connection`", "six")); } - @Test - public void caseInsensitiveRequestHeaders() throws IOException { + @RenderedSnippetTest + void caseInsensitiveRequestHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(this.operationBuilder.request("/").header("X-test", "test").build()); - assertThat(this.generatedSnippets.requestHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one")); + .document(operationBuilder.request("/").header("X-test", "test").build()); + assertThat(snippets.requestHeaders()) + .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); } - @Test - public void undocumentedRequestHeader() throws IOException { - new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(this.operationBuilder.request("http://localhost/") - .header("X-Test", "test") - .header("Accept", "*/*") - .build()); - assertThat(this.generatedSnippets.requestHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one")); + @RenderedSnippetTest + void undocumentedRequestHeader(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))).document( + operationBuilder.request("http://localhost/").header("X-Test", "test").header("Accept", "*/*").build()); + assertThat(snippets.requestHeaders()) + .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); } - @Test - public void requestHeadersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-headers")) - .willReturn(snippetResource("request-headers-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-headers", template = "request-headers-with-title") + void requestHeadersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one")), attributes(key("title").value("Custom title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .header("X-Test", "test") - .build()); - assertThat(this.generatedSnippets.requestHeaders()).contains("Custom title"); + .document(operationBuilder.request("http://localhost/").header("X-Test", "test").build()); + assertThat(snippets.requestHeaders()).contains("Custom title"); } - @Test - public void requestHeadersWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-headers")) - .willReturn(snippetResource("request-headers-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-headers", template = "request-headers-with-extra-column") + void requestHeadersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestHeadersSnippet( Arrays.asList(headerWithName("X-Test").description("one").attributes(key("foo").value("alpha")), headerWithName("Accept-Encoding").description("two").attributes(key("foo").value("bravo")), headerWithName("Accept").description("three").attributes(key("foo").value("charlie")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .header("X-Test", "test") .header("Accept-Encoding", "gzip, deflate") .header("Accept", "*/*") .build()); - assertThat(this.generatedSnippets.requestHeaders()).is(// - tableWithHeader("Name", "Description", "Foo").row("X-Test", "one", "alpha") - .row("Accept-Encoding", "two", "bravo") - .row("Accept", "three", "charlie")); + assertThat(snippets.requestHeaders()).isTable((table) -> table.withHeader("Name", "Description", "Foo") + .row("X-Test", "one", "alpha") + .row("Accept-Encoding", "two", "bravo") + .row("Accept", "three", "charlie")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { HeaderDocumentation .requestHeaders(headerWithName("X-Test").description("one"), headerWithName("Accept").description("two"), headerWithName("Accept-Encoding").description("three"), headerWithName("Accept-Language").description("four")) .and(headerWithName("Cache-Control").description("five"), headerWithName("Connection").description("six")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .header("X-Test", "test") .header("Accept", "*/*") .header("Accept-Encoding", "gzip, deflate") @@ -141,28 +124,39 @@ public void additionalDescriptors() throws IOException { .header("Cache-Control", "max-age=0") .header("Connection", "keep-alive") .build()); - assertThat(this.generatedSnippets.requestHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one") - .row("`Accept`", "two") - .row("`Accept-Encoding`", "three") - .row("`Accept-Language`", "four") - .row("`Cache-Control`", "five") - .row("`Connection`", "six")); + assertThat(snippets.requestHeaders()).isTable((table) -> table.withHeader("Name", "Description") + .row("`X-Test`", "one") + .row("`Accept`", "two") + .row("`Accept-Encoding`", "three") + .row("`Accept-Language`", "four") + .row("`Cache-Control`", "five") + .row("`Connection`", "six")); } - @Test - public void tableCellContentIsEscapedWhenNecessary() throws IOException { + @RenderedSnippetTest + void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestHeadersSnippet(Arrays.asList(headerWithName("Foo|Bar").description("one|two"))) - .document(this.operationBuilder.request("http://localhost/").header("Foo|Bar", "baz").build()); - assertThat(this.generatedSnippets.requestHeaders()).is(tableWithHeader("Name", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.request("http://localhost/").header("Foo|Bar", "baz").build()); + assertThat(snippets.requestHeaders()) + .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void missingRequestHeader(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description("one"))) + .document(operationBuilder.request("http://localhost/").build())) + .withMessage("Headers with the following names were not found in the request: [Accept]"); + } + + @SnippetTest + void undocumentedRequestHeaderAndMissingRequestHeader(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description("one"))) + .document(operationBuilder.request("http://localhost/").header("X-Test", "test").build())) + .withMessageEndingWith("Headers with the following names were not found in the request: [Accept]"); + } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java deleted file mode 100644 index 6efbe91d..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetFailureTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.util.Arrays; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; - -/** - * Tests for failures when rendering {@link ResponseHeadersSnippet} due to missing or - * undocumented headers. - * - * @author Andy Wilkinson - */ -public class ResponseHeadersSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void missingResponseHeader() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy( - () -> new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type").description("one"))) - .document(this.operationBuilder.response().build())) - .withMessage("Headers with the following names were not found" + " in the response: [Content-Type]"); - } - - @Test - public void undocumentedResponseHeaderAndMissingResponseHeader() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy( - () -> new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type").description("one"))) - .document(this.operationBuilder.response().header("X-Test", "test").build())) - .withMessageEndingWith("Headers with the following names were not found in the response: [Content-Type]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java index 4427d6c3..484ee4cb 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,18 +19,15 @@ import java.io.IOException; import java.util.Arrays; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -41,119 +38,120 @@ * @author Andreas Evers * @author Andy Wilkinson */ -public class ResponseHeadersSnippetTests extends AbstractSnippetTests { - - public ResponseHeadersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class ResponseHeadersSnippetTests { - @Test - public void responseWithHeaders() throws IOException { + @RenderedSnippetTest + void responseWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"), headerWithName("Content-Type").description("two"), headerWithName("Etag").description("three"), headerWithName("Cache-Control").description("five"), headerWithName("Vary").description("six"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .header("X-Test", "test") .header("Content-Type", "application/json") .header("Etag", "lskjadldj3ii32l2ij23") .header("Cache-Control", "max-age=0") .header("Vary", "User-Agent") .build()); - assertThat(this.generatedSnippets.responseHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one") - .row("`Content-Type`", "two") - .row("`Etag`", "three") - .row("`Cache-Control`", "five") - .row("`Vary`", "six")); + assertThat(snippets.responseHeaders()).isTable((table) -> table.withHeader("Name", "Description") + .row("`X-Test`", "one") + .row("`Content-Type`", "two") + .row("`Etag`", "three") + .row("`Cache-Control`", "five") + .row("`Vary`", "six")); } - @Test - public void caseInsensitiveResponseHeaders() throws IOException { + @RenderedSnippetTest + void caseInsensitiveResponseHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(this.operationBuilder.response().header("X-test", "test").build()); - assertThat(this.generatedSnippets.responseHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one")); + .document(operationBuilder.response().header("X-test", "test").build()); + assertThat(snippets.responseHeaders()) + .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); } - @Test - public void undocumentedResponseHeader() throws IOException { + @RenderedSnippetTest + void undocumentedResponseHeader(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(this.operationBuilder.response().header("X-Test", "test").header("Content-Type", "*/*").build()); - assertThat(this.generatedSnippets.responseHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one")); + .document(operationBuilder.response().header("X-Test", "test").header("Content-Type", "*/*").build()); + assertThat(snippets.responseHeaders()) + .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); } - @Test - public void responseHeadersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-headers")) - .willReturn(snippetResource("response-headers-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-headers", template = "response-headers-with-title") + void responseHeadersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one")), attributes(key("title").value("Custom title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() - .header("X-Test", "test") - .build()); - assertThat(this.generatedSnippets.responseHeaders()).contains("Custom title"); + .document(operationBuilder.response().header("X-Test", "test").build()); + assertThat(snippets.responseHeaders()).contains("Custom title"); } - @Test - public void responseHeadersWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-headers")) - .willReturn(snippetResource("response-headers-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-headers", template = "response-headers-with-extra-column") + void responseHeadersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseHeadersSnippet( Arrays.asList(headerWithName("X-Test").description("one").attributes(key("foo").value("alpha")), headerWithName("Content-Type").description("two").attributes(key("foo").value("bravo")), headerWithName("Etag").description("three").attributes(key("foo").value("charlie")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() + .document(operationBuilder.response() .header("X-Test", "test") .header("Content-Type", "application/json") .header("Etag", "lskjadldj3ii32l2ij23") .build()); - assertThat(this.generatedSnippets.responseHeaders()) - .is(tableWithHeader("Name", "Description", "Foo").row("X-Test", "one", "alpha") - .row("Content-Type", "two", "bravo") - .row("Etag", "three", "charlie")); + assertThat(snippets.responseHeaders()).isTable((table) -> table.withHeader("Name", "Description", "Foo") + .row("X-Test", "one", "alpha") + .row("Content-Type", "two", "bravo") + .row("Etag", "three", "charlie")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { HeaderDocumentation .responseHeaders(headerWithName("X-Test").description("one"), headerWithName("Content-Type").description("two"), headerWithName("Etag").description("three")) .and(headerWithName("Cache-Control").description("five"), headerWithName("Vary").description("six")) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .header("X-Test", "test") .header("Content-Type", "application/json") .header("Etag", "lskjadldj3ii32l2ij23") .header("Cache-Control", "max-age=0") .header("Vary", "User-Agent") .build()); - assertThat(this.generatedSnippets.responseHeaders()) - .is(tableWithHeader("Name", "Description").row("`X-Test`", "one") - .row("`Content-Type`", "two") - .row("`Etag`", "three") - .row("`Cache-Control`", "five") - .row("`Vary`", "six")); + assertThat(snippets.responseHeaders()).isTable((table) -> table.withHeader("Name", "Description") + .row("`X-Test`", "one") + .row("`Content-Type`", "two") + .row("`Etag`", "three") + .row("`Cache-Control`", "five") + .row("`Vary`", "six")); } - @Test - public void tableCellContentIsEscapedWhenNecessary() throws IOException { + @RenderedSnippetTest + void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseHeadersSnippet(Arrays.asList(headerWithName("Foo|Bar").description("one|two"))) - .document(this.operationBuilder.response().header("Foo|Bar", "baz").build()); - assertThat(this.generatedSnippets.responseHeaders()).is(tableWithHeader("Name", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.response().header("Foo|Bar", "baz").build()); + assertThat(snippets.responseHeaders()) + .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); + } + + @SnippetTest + void missingResponseHeader(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy( + () -> new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type").description("one"))) + .document(operationBuilder.response().build())) + .withMessage("Headers with the following names were not found" + " in the response: [Content-Type]"); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void undocumentedResponseHeaderAndMissingResponseHeader(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy( + () -> new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type").description("one"))) + .document(operationBuilder.response().header("X-Test", "test").build())) + .withMessageEndingWith("Headers with the following names were not found in the response: [Content-Type]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java index 447e00c0..a37e3fd3 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -18,20 +18,14 @@ import java.io.IOException; -import org.junit.Test; - import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -41,162 +35,160 @@ * @author Andy Wilkinson * @author Jonathan Pearlin */ -public class HttpRequestSnippetTests extends AbstractSnippetTests { +class HttpRequestSnippetTests { private static final String BOUNDARY = "6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm"; - public HttpRequestSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @Test - public void getRequest() throws IOException { + @RenderedSnippetTest + void getRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpRequestSnippet() - .document(this.operationBuilder.request("http://localhost/foo").header("Alpha", "a").build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost")); + .document(operationBuilder.request("http://localhost/foo").header("Alpha", "a").build()); + assertThat(snippets.httpRequest()) + .isHttpRequest((request) -> request.get("/foo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost")); } - @Test - public void getRequestWithQueryParameters() throws IOException { + @RenderedSnippetTest + void getRequestWithQueryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new HttpRequestSnippet() - .document(this.operationBuilder.request("http://localhost/foo?b=bravo").header("Alpha", "a").build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo?b=bravo").header("Alpha", "a") - .header(HttpHeaders.HOST, "localhost")); + .document(operationBuilder.request("http://localhost/foo?b=bravo").header("Alpha", "a").build()); + assertThat(snippets.httpRequest()).isHttpRequest( + (request) -> request.get("/foo?b=bravo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost")); } - @Test - public void getRequestWithPort() throws IOException { + @RenderedSnippetTest + void getRequestWithPort(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpRequestSnippet() - .document(this.operationBuilder.request("http://localhost:8080/foo").header("Alpha", "a").build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost:8080")); + .document(operationBuilder.request("http://localhost:8080/foo").header("Alpha", "a").build()); + assertThat(snippets.httpRequest()).isHttpRequest( + (request) -> request.get("/foo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost:8080")); } - @Test - public void getRequestWithCookies() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/foo") + @RenderedSnippetTest + void getRequestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo") .cookie("name1", "value1") .cookie("name2", "value2") .build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo").header(HttpHeaders.HOST, "localhost") - .header(HttpHeaders.COOKIE, "name1=value1; name2=value2")); + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.get("/foo") + .header(HttpHeaders.HOST, "localhost") + .header(HttpHeaders.COOKIE, "name1=value1; name2=value2")); } - @Test - public void getRequestWithQueryString() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/foo?bar=baz").build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo?bar=baz").header(HttpHeaders.HOST, "localhost")); + @RenderedSnippetTest + void getRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo?bar=baz").build()); + assertThat(snippets.httpRequest()) + .isHttpRequest((request) -> request.get("/foo?bar=baz").header(HttpHeaders.HOST, "localhost")); } - @Test - public void getRequestWithQueryStringWithNoValue() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/foo?bar").build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo?bar").header(HttpHeaders.HOST, "localhost")); + @RenderedSnippetTest + void getRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo?bar").build()); + assertThat(snippets.httpRequest()) + .isHttpRequest((request) -> request.get("/foo?bar").header(HttpHeaders.HOST, "localhost")); } - @Test - public void postRequestWithContent() throws IOException { + @RenderedSnippetTest + void postRequestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { String content = "Hello, world"; new HttpRequestSnippet() - .document(this.operationBuilder.request("http://localhost/foo").method("POST").content(content).build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.POST, "/foo").header(HttpHeaders.HOST, "localhost") - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); + .document(operationBuilder.request("http://localhost/foo").method("POST").content(content).build()); + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/foo") + .header(HttpHeaders.HOST, "localhost") + .content(content) + .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); } - @Test - public void postRequestWithContentAndQueryParameters() throws IOException { + @RenderedSnippetTest + void postRequestWithContentAndQueryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { String content = "Hello, world"; - new HttpRequestSnippet().document( - this.operationBuilder.request("http://localhost/foo?a=alpha").method("POST").content(content).build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.POST, "/foo?a=alpha").header(HttpHeaders.HOST, "localhost") - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); + new HttpRequestSnippet() + .document(operationBuilder.request("http://localhost/foo?a=alpha").method("POST").content(content).build()); + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/foo?a=alpha") + .header(HttpHeaders.HOST, "localhost") + .content(content) + .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); } - @Test - public void postRequestWithCharset() throws IOException { + @RenderedSnippetTest + void postRequestWithCharset(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; byte[] contentBytes = japaneseContent.getBytes("UTF-8"); - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/foo") + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo") .method("POST") .header("Content-Type", "text/plain;charset=UTF-8") .content(contentBytes) .build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.POST, "/foo").header("Content-Type", "text/plain;charset=UTF-8") - .header(HttpHeaders.HOST, "localhost") - .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length) - .content(japaneseContent)); + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/foo") + .header("Content-Type", "text/plain;charset=UTF-8") + .header(HttpHeaders.HOST, "localhost") + .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length) + .content(japaneseContent)); } - @Test - public void putRequestWithContent() throws IOException { + @RenderedSnippetTest + void putRequestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { String content = "Hello, world"; new HttpRequestSnippet() - .document(this.operationBuilder.request("http://localhost/foo").method("PUT").content(content).build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.PUT, "/foo").header(HttpHeaders.HOST, "localhost") - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); + .document(operationBuilder.request("http://localhost/foo").method("PUT").content(content).build()); + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.put("/foo") + .header(HttpHeaders.HOST, "localhost") + .content(content) + .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); } - @Test - public void multipartPost() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()) .build()); String expectedContent = createPart( String.format("Content-Disposition: " + "form-data; " + "name=image%n%n<< data >>")); - assertThat(this.generatedSnippets.httpRequest()).is(httpRequest(RequestMethod.POST, "/upload") + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/upload") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost") .content(expectedContent)); } - @Test - public void multipartPut() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPut(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") .method("PUT") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()) .build()); String expectedContent = createPart( String.format("Content-Disposition: " + "form-data; " + "name=image%n%n<< data >>")); - assertThat(this.generatedSnippets.httpRequest()).is(httpRequest(RequestMethod.PUT, "/upload") + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.put("/upload") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost") .content(expectedContent)); } - @Test - public void multipartPatch() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPatch(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") .method("PATCH") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()) .build()); String expectedContent = createPart( String.format("Content-Disposition: " + "form-data; " + "name=image%n%n<< data >>")); - assertThat(this.generatedSnippets.httpRequest()).is(httpRequest(RequestMethod.PATCH, "/upload") + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.patch("/upload") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost") .content(expectedContent)); } - @Test - public void multipartPostWithFilename() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPostWithFilename(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()) @@ -204,15 +196,16 @@ public void multipartPostWithFilename() throws IOException { .build()); String expectedContent = createPart(String .format("Content-Disposition: " + "form-data; " + "name=image; filename=image.png%n%n<< data >>")); - assertThat(this.generatedSnippets.httpRequest()).is(httpRequest(RequestMethod.POST, "/upload") + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/upload") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost") .content(expectedContent)); } - @Test - public void multipartPostWithContentType() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/upload") + @RenderedSnippetTest + void multipartPostWithContentType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") .method("POST") .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) .part("image", "<< data >>".getBytes()) @@ -220,38 +213,35 @@ public void multipartPostWithContentType() throws IOException { .build()); String expectedContent = createPart(String .format("Content-Disposition: form-data; name=image%nContent-Type: " + "image/png%n%n<< data >>")); - assertThat(this.generatedSnippets.httpRequest()).is(httpRequest(RequestMethod.POST, "/upload") + assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/upload") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) .header(HttpHeaders.HOST, "localhost") .content(expectedContent)); } - @Test - public void getRequestWithCustomHost() throws IOException { - new HttpRequestSnippet().document(this.operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.HOST, "api.example.com") - .build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.GET, "/foo").header(HttpHeaders.HOST, "api.example.com")); + @RenderedSnippetTest + void getRequestWithCustomHost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpRequestSnippet().document( + operationBuilder.request("http://localhost/foo").header(HttpHeaders.HOST, "api.example.com").build()); + assertThat(snippets.httpRequest()) + .isHttpRequest((request) -> request.get("/foo").header(HttpHeaders.HOST, "api.example.com")); } - @Test - public void requestWithCustomSnippetAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("http-request")).willReturn(snippetResource("http-request-with-title")); - new HttpRequestSnippet(attributes(key("title").value("Title for the request"))).document( - this.operationBuilder.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/foo") - .build()); - assertThat(this.generatedSnippets.httpRequest()).contains("Title for the request"); + @RenderedSnippetTest + @SnippetTemplate(snippet = "http-request", template = "http-request-with-title") + void requestWithCustomSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpRequestSnippet(attributes(key("title").value("Title for the request"))) + .document(operationBuilder.request("http://localhost/foo").build()); + assertThat(snippets.httpRequest()).contains("Title for the request"); } - @Test - public void deleteWithQueryString() throws IOException { + @RenderedSnippetTest + void deleteWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new HttpRequestSnippet() - .document(this.operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); - assertThat(this.generatedSnippets.httpRequest()) - .is(httpRequest(RequestMethod.DELETE, "/foo?a=alpha&b=bravo").header("Host", "localhost")); + .document(operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); + assertThat(snippets.httpRequest()) + .isHttpRequest((request) -> request.delete("/foo?a=alpha&b=bravo").header("Host", "localhost")); } private String createPart(String content) { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java index edd7e470..0ce5785e 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,21 +18,16 @@ import java.io.IOException; -import org.junit.Test; - import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -42,72 +37,66 @@ * @author Andy Wilkinson * @author Jonathan Pearlin */ -public class HttpResponseSnippetTests extends AbstractSnippetTests { - - public HttpResponseSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class HttpResponseSnippetTests { - @Test - public void basicResponse() throws IOException { - new HttpResponseSnippet().document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.httpResponse()).is(httpResponse(HttpStatus.OK)); + @RenderedSnippetTest + void basicResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpResponseSnippet().document(operationBuilder.build()); + assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok()); } - @Test - public void nonOkResponse() throws IOException { - new HttpResponseSnippet().document(this.operationBuilder.response().status(HttpStatus.BAD_REQUEST).build()); - assertThat(this.generatedSnippets.httpResponse()).is(httpResponse(HttpStatus.BAD_REQUEST)); + @RenderedSnippetTest + void nonOkResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpResponseSnippet().document(operationBuilder.response().status(HttpStatus.BAD_REQUEST).build()); + assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.badRequest()); } - @Test - public void responseWithHeaders() throws IOException { - new HttpResponseSnippet().document(this.operationBuilder.response() + @RenderedSnippetTest + void responseWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpResponseSnippet().document(operationBuilder.response() .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header("a", "alpha") .build()); - assertThat(this.generatedSnippets.httpResponse()) - .is(httpResponse(HttpStatus.OK).header("Content-Type", "application/json").header("a", "alpha")); + assertThat(snippets.httpResponse()).isHttpResponse( + (response) -> response.ok().header("Content-Type", "application/json").header("a", "alpha")); } - @Test - public void responseWithContent() throws IOException { + @RenderedSnippetTest + void responseWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { String content = "content"; - new HttpResponseSnippet().document(this.operationBuilder.response().content(content).build()); - assertThat(this.generatedSnippets.httpResponse()).is(httpResponse(HttpStatus.OK).content(content) + new HttpResponseSnippet().document(operationBuilder.response().content(content).build()); + assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok() + .content(content) .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); } - @Test - public void responseWithCharset() throws IOException { + @RenderedSnippetTest + void responseWithCharset(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; byte[] contentBytes = japaneseContent.getBytes("UTF-8"); - new HttpResponseSnippet().document(this.operationBuilder.response() + new HttpResponseSnippet().document(operationBuilder.response() .header("Content-Type", "text/plain;charset=UTF-8") .content(contentBytes) .build()); - assertThat(this.generatedSnippets.httpResponse()) - .is(httpResponse(HttpStatus.OK).header("Content-Type", "text/plain;charset=UTF-8") - .content(japaneseContent) - .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length)); + assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok() + .header("Content-Type", "text/plain;charset=UTF-8") + .content(japaneseContent) + .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length)); } - @Test - public void responseWithCustomSnippetAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("http-response")) - .willReturn(snippetResource("http-response-with-title")); - new HttpResponseSnippet(attributes(key("title").value("Title for the response"))).document( - this.operationBuilder.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .build()); - assertThat(this.generatedSnippets.httpResponse()).contains("Title for the response"); + @RenderedSnippetTest + @SnippetTemplate(snippet = "http-response", template = "http-response-with-title") + void responseWithCustomSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new HttpResponseSnippet(attributes(key("title").value("Title for the response"))) + .document(operationBuilder.build()); + assertThat(snippets.httpResponse()).contains("Title for the response"); } - @Test - public void responseWithCustomStatus() throws IOException { - new HttpResponseSnippet() - .document(this.operationBuilder.response().status(HttpStatusCode.valueOf(215)).build()); - assertThat(this.generatedSnippets.httpResponse()).is(httpResponse(215)); + @RenderedSnippetTest + void responseWithCustomStatus(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpResponseSnippet().document(operationBuilder.response().status(HttpStatusCode.valueOf(215)).build()); + assertThat(snippets.httpResponse()).isHttpResponse(((response) -> response.status(215))); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java index f1540757..2e51b0ac 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,7 +20,7 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -37,18 +37,18 @@ * * @author Andy Wilkinson */ -public class ContentTypeLinkExtractorTests { +class ContentTypeLinkExtractorTests { private final OperationResponseFactory responseFactory = new OperationResponseFactory(); @Test - public void extractionFailsWithNullContentType() { + void extractionFailsWithNullContentType() { assertThatIllegalStateException().isThrownBy(() -> new ContentTypeLinkExtractor() .extractLinks(this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), null))); } @Test - public void extractorCalledWithMatchingContextType() throws IOException { + void extractorCalledWithMatchingContextType() throws IOException { Map extractors = new HashMap<>(); LinkExtractor extractor = mock(LinkExtractor.class); extractors.put(MediaType.APPLICATION_JSON, extractor); @@ -60,7 +60,7 @@ public void extractorCalledWithMatchingContextType() throws IOException { } @Test - public void extractorCalledWithCompatibleContextType() throws IOException { + void extractorCalledWithCompatibleContextType() throws IOException { Map extractors = new HashMap<>(); LinkExtractor extractor = mock(LinkExtractor.class); extractors.put(MediaType.APPLICATION_JSON, extractor); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java index bf8a05fc..5346d512 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -24,10 +24,9 @@ import java.util.List; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.http.HttpStatus; import org.springframework.restdocs.operation.OperationResponse; @@ -39,13 +38,13 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Parameterized tests for {@link HalLinkExtractor} and {@link AtomLinkExtractor} with - * various payloads. + * Tests for {@link HalLinkExtractor} and {@link AtomLinkExtractor} with various payloads. * * @author Andy Wilkinson */ -@RunWith(Parameterized.class) -public class LinkExtractorsPayloadTests { +@ParameterizedClass(name = "{1}") +@MethodSource("parameters") +class LinkExtractorsPayloadTests { private final OperationResponseFactory responseFactory = new OperationResponseFactory(); @@ -53,25 +52,24 @@ public class LinkExtractorsPayloadTests { private final String linkType; - @Parameters(name = "{1}") - public static Collection data() { + static Collection parameters() { return Arrays.asList(new Object[] { new HalLinkExtractor(), "hal" }, new Object[] { new AtomLinkExtractor(), "atom" }); } - public LinkExtractorsPayloadTests(LinkExtractor linkExtractor, String linkType) { + LinkExtractorsPayloadTests(LinkExtractor linkExtractor, String linkType) { this.linkExtractor = linkExtractor; this.linkType = linkType; } @Test - public void singleLink() throws IOException { + void singleLink() throws IOException { Map> links = this.linkExtractor.extractLinks(createResponse("single-link")); assertLinks(Arrays.asList(new Link("alpha", "https://alpha.example.com/", "Alpha")), links); } @Test - public void multipleLinksWithDifferentRels() throws IOException { + void multipleLinksWithDifferentRels() throws IOException { Map> links = this.linkExtractor .extractLinks(createResponse("multiple-links-different-rels")); assertLinks(Arrays.asList(new Link("alpha", "https://alpha.example.com/", "Alpha"), @@ -79,20 +77,20 @@ public void multipleLinksWithDifferentRels() throws IOException { } @Test - public void multipleLinksWithSameRels() throws IOException { + void multipleLinksWithSameRels() throws IOException { Map> links = this.linkExtractor.extractLinks(createResponse("multiple-links-same-rels")); assertLinks(Arrays.asList(new Link("alpha", "https://alpha.example.com/one", "Alpha one"), new Link("alpha", "https://alpha.example.com/two")), links); } @Test - public void noLinks() throws IOException { + void noLinks() throws IOException { Map> links = this.linkExtractor.extractLinks(createResponse("no-links")); assertLinks(Collections.emptyList(), links); } @Test - public void linksInTheWrongFormat() throws IOException { + void linksInTheWrongFormat() throws IOException { Map> links = this.linkExtractor.extractLinks(createResponse("wrong-format")); assertLinks(Collections.emptyList(), links); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java deleted file mode 100644 index d62a1d80..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetFailureTests.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for failures when rendering {@link LinksSnippet} due to missing or undocumented - * links. - * - * @author Andy Wilkinson - */ -public class LinksSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedLink() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), - Collections.emptyList()) - .document(this.operationBuilder.build())) - .withMessage("Links with the following relations were not documented: [foo]"); - } - - @Test - public void missingLink() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor(), - Arrays.asList(new LinkDescriptor("foo").description("bar"))) - .document(this.operationBuilder.build())) - .withMessage("Links with the following relations were not found in the response: [foo]"); - } - - @Test - public void undocumentedLinkAndMissingLink() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")), - Arrays.asList(new LinkDescriptor("foo").description("bar"))) - .document(this.operationBuilder.build())) - .withMessage("Links with the following relations were not documented: [a]. Links with the following" - + " relations were not found in the response: [foo]"); - } - - @Test - public void linkWithNoDescription() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), - Arrays.asList(new LinkDescriptor("foo"))) - .document(this.operationBuilder.build())) - .withMessage("No description was provided for the link with rel 'foo' and no title was available" - + " from the link in the payload"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java index b71ab843..7782ce42 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,17 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -39,115 +37,145 @@ * * @author Andy Wilkinson */ -public class LinksSnippetTests extends AbstractSnippetTests { - - public LinksSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class LinksSnippetTests { - @Test - public void ignoredLink() throws IOException { + @RenderedSnippetTest + void ignoredLink(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("a").ignored(), new LinkDescriptor("b").description("Link b"))) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()).is(tableWithHeader("Relation", "Description").row("`b`", "Link b")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`b`", "Link b")); } - @Test - public void allUndocumentedLinksCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedLinksCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("b").description("Link b")), true) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()).is(tableWithHeader("Relation", "Description").row("`b`", "Link b")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`b`", "Link b")); } - @Test - public void presentOptionalLink() throws IOException { + @RenderedSnippetTest + void presentOptionalLink(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "blah")), Arrays.asList(new LinkDescriptor("foo").description("bar").optional())) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()).is(tableWithHeader("Relation", "Description").row("`foo`", "bar")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`foo`", "bar")); } - @Test - public void missingOptionalLink() throws IOException { + @RenderedSnippetTest + void missingOptionalLink(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo").description("bar").optional())) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()).is(tableWithHeader("Relation", "Description").row("`foo`", "bar")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`foo`", "bar")); } - @Test - public void documentedLinks() throws IOException { + @RenderedSnippetTest + void documentedLinks(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("a").description("one"), new LinkDescriptor("b").description("two"))) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()) - .is(tableWithHeader("Relation", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void linkDescriptionFromTitleInPayload() throws IOException { + @RenderedSnippetTest + void linkDescriptionFromTitleInPayload(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new LinksSnippet( new StubLinkExtractor().withLinks(new Link("a", "alpha", "Link a"), new Link("b", "bravo", "Link b")), Arrays.asList(new LinkDescriptor("a").description("one"), new LinkDescriptor("b"))) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()) - .is(tableWithHeader("Relation", "Description").row("`a`", "one").row("`b`", "Link b")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`a`", "one").row("`b`", "Link b")); } - @Test - public void linksWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("links")).willReturn(snippetResource("links-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "links", template = "links-with-title") + void linksWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("a").description("one"), new LinkDescriptor("b").description("two")), attributes(key("title").value("Title for the links"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .build()); - assertThat(this.generatedSnippets.links()).contains("Title for the links"); + .document(operationBuilder.build()); + assertThat(snippets.links()).contains("Title for the links"); } - @Test - public void linksWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("links")).willReturn(snippetResource("links-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "links", template = "links-with-extra-column") + void linksWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("a").description("one").attributes(key("foo").value("alpha")), new LinkDescriptor("b").description("two").attributes(key("foo").value("bravo")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .build()); - assertThat(this.generatedSnippets.links()) - .is(tableWithHeader("Relation", "Description", "Foo").row("a", "one", "alpha").row("b", "two", "bravo")); + .document(operationBuilder.build()); + assertThat(snippets.links()).isTable((table) -> table.withHeader("Relation", "Description", "Foo") + .row("a", "one", "alpha") + .row("b", "two", "bravo")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { HypermediaDocumentation .links(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), new LinkDescriptor("a").description("one")) .and(new LinkDescriptor("b").description("two")) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()) - .is(tableWithHeader("Relation", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void tableCellContentIsEscapedWhenNecessary() throws IOException { + @RenderedSnippetTest + void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new LinksSnippet(new StubLinkExtractor().withLinks(new Link("Foo|Bar", "foo")), Arrays.asList(new LinkDescriptor("Foo|Bar").description("one|two"))) - .document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.links()).is(tableWithHeader("Relation", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.build()); + assertThat(snippets.links()) + .isTable((table) -> table.withHeader("Relation", "Description").row("`Foo|Bar`", "one|two")); + } + + @SnippetTest + void undocumentedLink(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), + Collections.emptyList()) + .document(operationBuilder.build())) + .withMessage("Links with the following relations were not documented: [foo]"); + } + + @SnippetTest + void missingLink(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor(), + Arrays.asList(new LinkDescriptor("foo").description("bar"))) + .document(operationBuilder.build())) + .withMessage("Links with the following relations were not found in the response: [foo]"); + } + + @SnippetTest + void undocumentedLinkAndMissingLink(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")), + Arrays.asList(new LinkDescriptor("foo").description("bar"))) + .document(operationBuilder.build())) + .withMessage("Links with the following relations were not documented: [a]. Links with the following" + + " relations were not found in the response: [foo]"); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void linkWithNoDescription(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), + Arrays.asList(new LinkDescriptor("foo"))) + .document(operationBuilder.build())) + .withMessage("No description was provided for the link with rel 'foo' and no title was available" + + " from the link in the payload"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java index dbbee922..107a4d0d 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.net.URI; import java.util.Collections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -39,7 +39,7 @@ * @author Andy Wilkinson * */ -public class ContentModifyingOperationPreprocessorTests { +class ContentModifyingOperationPreprocessorTests { private final OperationRequestFactory requestFactory = new OperationRequestFactory(); @@ -56,7 +56,7 @@ public byte[] modifyContent(byte[] originalContent, MediaType mediaType) { }); @Test - public void modifyRequestContent() { + void modifyRequestContent() { OperationRequest request = this.requestFactory.create(URI.create("http://localhost/"), HttpMethod.GET, "content".getBytes(), new HttpHeaders(), Collections.emptyList()); OperationRequest preprocessed = this.preprocessor.preprocess(request); @@ -64,7 +64,7 @@ public void modifyRequestContent() { } @Test - public void modifyResponseContent() { + void modifyResponseContent() { OperationResponse response = this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), "content".getBytes()); OperationResponse preprocessed = this.preprocessor.preprocess(response); @@ -72,7 +72,7 @@ public void modifyResponseContent() { } @Test - public void contentLengthIsUpdated() { + void contentLengthIsUpdated() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentLength(7); OperationRequest request = this.requestFactory.create(URI.create("http://localhost/"), HttpMethod.GET, diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java index b9f39844..a395d640 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,7 +18,7 @@ import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.operation.OperationRequest; @@ -31,10 +31,10 @@ * * @author Andy Wilkinson */ -public class DelegatingOperationRequestPreprocessorTests { +class DelegatingOperationRequestPreprocessorTests { @Test - public void delegationOccurs() { + void delegationOccurs() { OperationRequest originalRequest = mock(OperationRequest.class); OperationPreprocessor preprocessor1 = mock(OperationPreprocessor.class); OperationRequest preprocessedRequest1 = mock(OperationRequest.class); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java index 2a2bb506..513eeb86 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,7 +18,7 @@ import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.operation.OperationResponse; @@ -31,10 +31,10 @@ * * @author Andy Wilkinson */ -public class DelegatingOperationResponsePreprocessorTests { +class DelegatingOperationResponsePreprocessorTests { @Test - public void delegationOccurs() { + void delegationOccurs() { OperationResponse originalResponse = mock(OperationResponse.class); OperationPreprocessor preprocessor1 = mock(OperationPreprocessor.class); OperationResponse preprocessedResponse1 = mock(OperationResponse.class); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java index 5d3e77ac..b68b27ff 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.function.Consumer; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -41,12 +41,12 @@ * @author Jihoon Cha * @author Andy Wilkinson */ -public class HeadersModifyingOperationPreprocessorTests { +class HeadersModifyingOperationPreprocessorTests { private final HeadersModifyingOperationPreprocessor preprocessor = new HeadersModifyingOperationPreprocessor(); @Test - public void addNewHeader() { + void addNewHeader() { this.preprocessor.add("a", "alpha"); assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().get("a")) .isEqualTo(Arrays.asList("alpha")); @@ -55,7 +55,7 @@ public void addNewHeader() { } @Test - public void addValueToExistingHeader() { + void addValueToExistingHeader() { this.preprocessor.add("a", "alpha"); assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) .getHeaders() @@ -66,7 +66,7 @@ public void addValueToExistingHeader() { } @Test - public void setNewHeader() { + void setNewHeader() { this.preprocessor.set("a", "alpha", "avocado"); assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerSet()) .contains(entry("a", Arrays.asList("alpha", "avocado"))); @@ -75,7 +75,7 @@ public void setNewHeader() { } @Test - public void setExistingHeader() { + void setExistingHeader() { this.preprocessor.set("a", "alpha", "avocado"); assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) .getHeaders() @@ -86,14 +86,14 @@ public void setExistingHeader() { } @Test - public void removeNonExistentHeader() { + void removeNonExistentHeader() { this.preprocessor.remove("a"); assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerNames()).doesNotContain("a"); assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerNames()).doesNotContain("a"); } @Test - public void removeHeader() { + void removeHeader() { this.preprocessor.remove("a"); assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) .getHeaders() @@ -104,14 +104,14 @@ public void removeHeader() { } @Test - public void removeHeaderValueForNonExistentHeader() { + void removeHeaderValueForNonExistentHeader() { this.preprocessor.remove("a", "apple"); assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerNames()).doesNotContain("a"); assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerNames()).doesNotContain("a"); } @Test - public void removeHeaderValueWithMultipleValues() { + void removeHeaderValueWithMultipleValues() { this.preprocessor.remove("a", "apple"); assertThat( this.preprocessor.preprocess(createRequest((headers) -> headers.addAll("a", List.of("apple", "alpha")))) @@ -125,7 +125,7 @@ public void removeHeaderValueWithMultipleValues() { } @Test - public void removeHeaderValueWithSingleValueRemovesEntryEntirely() { + void removeHeaderValueWithSingleValueRemovesEntryEntirely() { this.preprocessor.remove("a", "apple"); assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) .getHeaders() @@ -136,7 +136,7 @@ public void removeHeaderValueWithSingleValueRemovesEntryEntirely() { } @Test - public void removeHeadersByNamePattern() { + void removeHeadersByNamePattern() { Consumer headersCustomizer = (headers) -> { headers.add("apple", "apple"); headers.add("alpha", "alpha"); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java index ad13b255..7fe262d1 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -26,7 +26,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.hypermedia.Link; @@ -38,7 +38,7 @@ * @author Andy Wilkinson * */ -public class LinkMaskingContentModifierTests { +class LinkMaskingContentModifierTests { private final ContentModifier contentModifier = new LinkMaskingContentModifier(); @@ -47,38 +47,38 @@ public class LinkMaskingContentModifierTests { private final Link[] maskedLinks = new Link[] { new Link("a", "..."), new Link("b", "...") }; @Test - public void halLinksAreMasked() throws Exception { + void halLinksAreMasked() throws Exception { assertThat(this.contentModifier.modifyContent(halPayloadWithLinks(this.links), null)) .isEqualTo(halPayloadWithLinks(this.maskedLinks)); } @Test - public void formattedHalLinksAreMasked() throws Exception { + void formattedHalLinksAreMasked() throws Exception { assertThat(this.contentModifier.modifyContent(formattedHalPayloadWithLinks(this.links), null)) .isEqualTo(formattedHalPayloadWithLinks(this.maskedLinks)); } @Test - public void atomLinksAreMasked() throws Exception { + void atomLinksAreMasked() throws Exception { assertThat(this.contentModifier.modifyContent(atomPayloadWithLinks(this.links), null)) .isEqualTo(atomPayloadWithLinks(this.maskedLinks)); } @Test - public void formattedAtomLinksAreMasked() throws Exception { + void formattedAtomLinksAreMasked() throws Exception { assertThat(this.contentModifier.modifyContent(formattedAtomPayloadWithLinks(this.links), null)) .isEqualTo(formattedAtomPayloadWithLinks(this.maskedLinks)); } @Test - public void maskCanBeCustomized() throws Exception { + void maskCanBeCustomized() throws Exception { assertThat( new LinkMaskingContentModifier("custom").modifyContent(formattedAtomPayloadWithLinks(this.links), null)) .isEqualTo(formattedAtomPayloadWithLinks(new Link("a", "custom"), new Link("b", "custom"))); } @Test - public void maskCanUseUtf8Characters() throws Exception { + void maskCanUseUtf8Characters() throws Exception { String ellipsis = "\u2026"; assertThat( new LinkMaskingContentModifier(ellipsis).modifyContent(formattedHalPayloadWithLinks(this.links), null)) diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java index 838a045e..61c24b77 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,7 +20,7 @@ import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; @@ -31,10 +31,10 @@ * * @author Andy Wilkinson */ -public class PatternReplacingContentModifierTests { +class PatternReplacingContentModifierTests { @Test - public void patternsAreReplaced() { + void patternsAreReplaced() { Pattern pattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", Pattern.CASE_INSENSITIVE); PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>"); @@ -44,7 +44,7 @@ public void patternsAreReplaced() { } @Test - public void contentThatDoesNotMatchIsUnchanged() { + void contentThatDoesNotMatchIsUnchanged() { Pattern pattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", Pattern.CASE_INSENSITIVE); PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>"); @@ -53,7 +53,7 @@ public void contentThatDoesNotMatchIsUnchanged() { } @Test - public void encodingIsPreservedUsingCharsetFromContentType() { + void encodingIsPreservedUsingCharsetFromContentType() { String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; Pattern pattern = Pattern.compile("[0-9]+"); PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>"); @@ -63,7 +63,7 @@ public void encodingIsPreservedUsingCharsetFromContentType() { } @Test - public void encodingIsPreservedUsingFallbackCharset() { + void encodingIsPreservedUsingFallbackCharset() { String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; Pattern pattern = Pattern.compile("[0-9]+"); PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>", diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java index 386624a6..a0c28376 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,10 +20,11 @@ import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.testfixtures.OutputCaptureRule; +import org.springframework.restdocs.testfixtures.jupiter.CapturedOutput; +import org.springframework.restdocs.testfixtures.jupiter.OutputCaptureExtension; import static org.assertj.core.api.Assertions.assertThat; @@ -33,19 +34,17 @@ * @author Andy Wilkinson * */ -public class PrettyPrintingContentModifierTests { - - @Rule - public OutputCaptureRule outputCapture = new OutputCaptureRule(); +@ExtendWith(OutputCaptureExtension.class) +class PrettyPrintingContentModifierTests { @Test - public void prettyPrintJson() { + void prettyPrintJson() { assertThat(new PrettyPrintingContentModifier().modifyContent("{\"a\":5}".getBytes(), null)) .isEqualTo(String.format("{%n \"a\" : 5%n}").getBytes()); } @Test - public void prettyPrintXml() { + void prettyPrintXml() { assertThat(new PrettyPrintingContentModifier() .modifyContent("".getBytes(), null)) .isEqualTo(String @@ -55,28 +54,28 @@ public void prettyPrintXml() { } @Test - public void empytContentIsHandledGracefully() { + void empytContentIsHandledGracefully() { assertThat(new PrettyPrintingContentModifier().modifyContent("".getBytes(), null)).isEqualTo("".getBytes()); } @Test - public void nonJsonAndNonXmlContentIsHandledGracefully() { + void nonJsonAndNonXmlContentIsHandledGracefully(CapturedOutput output) { String content = "abcdefg"; assertThat(new PrettyPrintingContentModifier().modifyContent(content.getBytes(), null)) .isEqualTo(content.getBytes()); - assertThat(this.outputCapture).isEmpty(); + assertThat(output).isEmpty(); } @Test - public void nonJsonContentThatInitiallyLooksLikeJsonIsHandledGracefully() { + void nonJsonContentThatInitiallyLooksLikeJsonIsHandledGracefully(CapturedOutput output) { String content = "\"abc\",\"def\""; assertThat(new PrettyPrintingContentModifier().modifyContent(content.getBytes(), null)) .isEqualTo(content.getBytes()); - assertThat(this.outputCapture).isEmpty(); + assertThat(output).isEmpty(); } @Test - public void encodingIsPreserved() throws Exception { + void encodingIsPreserved() throws Exception { Map input = new HashMap<>(); input.put("japanese", "\u30b3\u30f3\u30c6\u30f3\u30c4"); ObjectMapper objectMapper = new ObjectMapper(); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java index 312167a2..0c5724eb 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -41,7 +41,7 @@ * * @author Andy Wilkinson */ -public class UriModifyingOperationPreprocessorTests { +class UriModifyingOperationPreprocessorTests { private final OperationRequestFactory requestFactory = new OperationRequestFactory(); @@ -50,14 +50,14 @@ public class UriModifyingOperationPreprocessorTests { private final UriModifyingOperationPreprocessor preprocessor = new UriModifyingOperationPreprocessor(); @Test - public void requestUriSchemeCanBeModified() { + void requestUriSchemeCanBeModified() { this.preprocessor.scheme("https"); OperationRequest processed = this.preprocessor.preprocess(createRequestWithUri("http://localhost:12345/")); assertThat(processed.getUri()).isEqualTo(URI.create("https://localhost:12345/")); } @Test - public void requestUriHostCanBeModified() { + void requestUriHostCanBeModified() { this.preprocessor.host("api.example.com"); OperationRequest processed = this.preprocessor.preprocess(createRequestWithUri("https://api.foo.com:12345/")); assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com:12345/")); @@ -65,7 +65,7 @@ public void requestUriHostCanBeModified() { } @Test - public void requestUriPortCanBeModified() { + void requestUriPortCanBeModified() { this.preprocessor.port(23456); OperationRequest processed = this.preprocessor .preprocess(createRequestWithUri("https://api.example.com:12345/")); @@ -74,7 +74,7 @@ public void requestUriPortCanBeModified() { } @Test - public void requestUriPortCanBeRemoved() { + void requestUriPortCanBeRemoved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithUri("https://api.example.com:12345/")); @@ -83,7 +83,7 @@ public void requestUriPortCanBeRemoved() { } @Test - public void requestUriPathIsPreserved() { + void requestUriPathIsPreserved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithUri("https://api.example.com:12345/foo/bar")); @@ -91,7 +91,7 @@ public void requestUriPathIsPreserved() { } @Test - public void requestUriQueryIsPreserved() { + void requestUriQueryIsPreserved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithUri("https://api.example.com:12345/?foo=bar")); @@ -99,7 +99,7 @@ public void requestUriQueryIsPreserved() { } @Test - public void requestUriAnchorIsPreserved() { + void requestUriAnchorIsPreserved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithUri("https://api.example.com:12345/#foo")); @@ -107,7 +107,7 @@ public void requestUriAnchorIsPreserved() { } @Test - public void requestContentUriSchemeCanBeModified() { + void requestContentUriSchemeCanBeModified() { this.preprocessor.scheme("https"); OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( "The uri 'https://localhost:12345/' should be used. foo:bar will be unaffected")); @@ -116,7 +116,7 @@ public void requestContentUriSchemeCanBeModified() { } @Test - public void requestContentUriHostCanBeModified() { + void requestContentUriHostCanBeModified() { this.preprocessor.host("api.example.com"); OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( "The uri 'https://localhost:12345/' should be used. foo:bar will be unaffected")); @@ -125,7 +125,7 @@ public void requestContentUriHostCanBeModified() { } @Test - public void requestContentHostOfUriWithoutPortCanBeModified() { + void requestContentHostOfUriWithoutPortCanBeModified() { this.preprocessor.host("api.example.com"); OperationRequest processed = this.preprocessor.preprocess( createRequestWithContent("The uri 'https://localhost/' should be used. foo:bar will be unaffected")); @@ -134,7 +134,7 @@ public void requestContentHostOfUriWithoutPortCanBeModified() { } @Test - public void requestContentUriPortCanBeAdded() { + void requestContentUriPortCanBeAdded() { this.preprocessor.port(23456); OperationRequest processed = this.preprocessor.preprocess( createRequestWithContent("The uri 'http://localhost/' should be used. foo:bar will be unaffected")); @@ -143,7 +143,7 @@ public void requestContentUriPortCanBeAdded() { } @Test - public void requestContentUriPortCanBeModified() { + void requestContentUriPortCanBeModified() { this.preprocessor.port(23456); OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( "The uri 'http://localhost:12345/' should be used. foo:bar will be unaffected")); @@ -152,7 +152,7 @@ public void requestContentUriPortCanBeModified() { } @Test - public void requestContentUriPortCanBeRemoved() { + void requestContentUriPortCanBeRemoved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( "The uri 'http://localhost:12345/' should be used. foo:bar will be unaffected")); @@ -161,7 +161,7 @@ public void requestContentUriPortCanBeRemoved() { } @Test - public void multipleRequestContentUrisCanBeModified() { + void multipleRequestContentUrisCanBeModified() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( "Use 'http://localhost:12345/' or 'https://localhost:23456/' to access the service")); @@ -170,7 +170,7 @@ public void multipleRequestContentUrisCanBeModified() { } @Test - public void requestContentUriPathIsPreserved() { + void requestContentUriPathIsPreserved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithContent("The uri 'http://localhost:12345/foo/bar' should be used")); @@ -178,7 +178,7 @@ public void requestContentUriPathIsPreserved() { } @Test - public void requestContentUriQueryIsPreserved() { + void requestContentUriQueryIsPreserved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithContent("The uri 'http://localhost:12345/?foo=bar' should be used")); @@ -186,7 +186,7 @@ public void requestContentUriQueryIsPreserved() { } @Test - public void requestContentUriAnchorIsPreserved() { + void requestContentUriAnchorIsPreserved() { this.preprocessor.removePort(); OperationRequest processed = this.preprocessor .preprocess(createRequestWithContent("The uri 'http://localhost:12345/#foo' should be used")); @@ -194,7 +194,7 @@ public void requestContentUriAnchorIsPreserved() { } @Test - public void responseContentUriSchemeCanBeModified() { + void responseContentUriSchemeCanBeModified() { this.preprocessor.scheme("https"); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'http://localhost:12345/' should be used")); @@ -202,7 +202,7 @@ public void responseContentUriSchemeCanBeModified() { } @Test - public void responseContentUriHostCanBeModified() { + void responseContentUriHostCanBeModified() { this.preprocessor.host("api.example.com"); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'https://localhost:12345/' should be used")); @@ -211,7 +211,7 @@ public void responseContentUriHostCanBeModified() { } @Test - public void responseContentUriPortCanBeModified() { + void responseContentUriPortCanBeModified() { this.preprocessor.port(23456); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'http://localhost:12345/' should be used")); @@ -219,7 +219,7 @@ public void responseContentUriPortCanBeModified() { } @Test - public void responseContentUriPortCanBeRemoved() { + void responseContentUriPortCanBeRemoved() { this.preprocessor.removePort(); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'http://localhost:12345/' should be used")); @@ -227,7 +227,7 @@ public void responseContentUriPortCanBeRemoved() { } @Test - public void multipleResponseContentUrisCanBeModified() { + void multipleResponseContentUrisCanBeModified() { this.preprocessor.removePort(); OperationResponse processed = this.preprocessor.preprocess(createResponseWithContent( "Use 'http://localhost:12345/' or 'https://localhost:23456/' to access the service")); @@ -236,7 +236,7 @@ public void multipleResponseContentUrisCanBeModified() { } @Test - public void responseContentUriPathIsPreserved() { + void responseContentUriPathIsPreserved() { this.preprocessor.removePort(); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'http://localhost:12345/foo/bar' should be used")); @@ -244,7 +244,7 @@ public void responseContentUriPathIsPreserved() { } @Test - public void responseContentUriQueryIsPreserved() { + void responseContentUriQueryIsPreserved() { this.preprocessor.removePort(); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'http://localhost:12345/?foo=bar' should be used")); @@ -252,7 +252,7 @@ public void responseContentUriQueryIsPreserved() { } @Test - public void responseContentUriAnchorIsPreserved() { + void responseContentUriAnchorIsPreserved() { this.preprocessor.removePort(); OperationResponse processed = this.preprocessor .preprocess(createResponseWithContent("The uri 'http://localhost:12345/#foo' should be used")); @@ -260,7 +260,7 @@ public void responseContentUriAnchorIsPreserved() { } @Test - public void urisInRequestHeadersCanBeModified() { + void urisInRequestHeadersCanBeModified() { OperationRequest processed = this.preprocessor.host("api.example.com") .preprocess(createRequestWithHeader("Foo", "https://locahost:12345/")); assertThat(processed.getHeaders().getFirst("Foo")).isEqualTo("https://api.example.com:12345/"); @@ -268,14 +268,14 @@ public void urisInRequestHeadersCanBeModified() { } @Test - public void urisInResponseHeadersCanBeModified() { + void urisInResponseHeadersCanBeModified() { OperationResponse processed = this.preprocessor.host("api.example.com") .preprocess(createResponseWithHeader("Foo", "https://locahost:12345/")); assertThat(processed.getHeaders().getFirst("Foo")).isEqualTo("https://api.example.com:12345/"); } @Test - public void urisInRequestPartHeadersCanBeModified() { + void urisInRequestPartHeadersCanBeModified() { OperationRequest processed = this.preprocessor.host("api.example.com") .preprocess(createRequestWithPartWithHeader("Foo", "https://locahost:12345/")); assertThat(processed.getParts().iterator().next().getHeaders().getFirst("Foo")) @@ -283,7 +283,7 @@ public void urisInRequestPartHeadersCanBeModified() { } @Test - public void urisInRequestPartContentCanBeModified() { + void urisInRequestPartContentCanBeModified() { OperationRequest processed = this.preprocessor.host("api.example.com") .preprocess(createRequestWithPartWithContent("The uri 'https://localhost:12345/' should be used")); assertThat(new String(processed.getParts().iterator().next().getContent())) @@ -291,7 +291,7 @@ public void urisInRequestPartContentCanBeModified() { } @Test - public void modifiedUriDoesNotGetDoubleEncoded() { + void modifiedUriDoesNotGetDoubleEncoded() { this.preprocessor.scheme("https"); OperationRequest processed = this.preprocessor .preprocess(createRequestWithUri("http://localhost:12345/?foo=%7B%7D")); @@ -300,7 +300,7 @@ public void modifiedUriDoesNotGetDoubleEncoded() { } @Test - public void resultingRequestHasCookiesFromOriginalRequst() { + void resultingRequestHasCookiesFromOriginalRequst() { List cookies = Arrays.asList(new RequestCookie("a", "alpha")); OperationRequest request = this.requestFactory.create(URI.create("http://localhost:12345/"), HttpMethod.GET, new byte[0], new HttpHeaders(), Collections.emptyList(), cookies); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java index 8c1144d5..ce094546 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,21 +19,13 @@ import java.io.IOException; import java.util.Arrays; -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.testfixtures.GeneratedSnippets; -import org.springframework.restdocs.testfixtures.OperationBuilder; -import org.springframework.restdocs.testfixtures.SnippetConditions; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest.Format; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; /** @@ -41,33 +33,17 @@ * * @author Andy Wilkinson */ -public class AsciidoctorRequestFieldsSnippetTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Rule - public GeneratedSnippets generatedSnippets = new GeneratedSnippets(TemplateFormats.asciidoctor()); - - @Test - public void requestFieldsWithListDescription() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-fields")) - .willReturn(snippetResource("request-fields-with-list-description")); - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description(Arrays.asList("one", "two")))).document( - this.operationBuilder.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .content("{\"a\": \"foo\"}") - .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(SnippetConditions.tableWithHeader(TemplateFormats.asciidoctor(), "Path", "Type", "Description") - // - .row("a", "String", String.format(" - one%n - two")) - .configuration("[cols=\"1,1,1a\"]")); - } - - private FileSystemResource snippetResource(String name) { - return new FileSystemResource("src/test/resources/custom-snippet-templates/asciidoctor/" + name + ".snippet"); +class AsciidoctorRequestFieldsSnippetTests { + + @RenderedSnippetTest(format = Format.ASCIIDOCTOR) + @SnippetTemplate(snippet = "request-fields", template = "request-fields-with-list-description") + void requestFieldsWithListDescription(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description(Arrays.asList("one", "two")))) + .document(operationBuilder.request("http://localhost/").content("{\"a\": \"foo\"}").build()); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("a", "String", String.format(" - one%n - two")) + .configuration("[cols=\"1,1,1a\"]")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java index 55c6638b..a5d0b958 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -25,7 +25,7 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; @@ -38,11 +38,11 @@ * * @author Andy Wilkinson */ -public class FieldPathPayloadSubsectionExtractorTests { +class FieldPathPayloadSubsectionExtractorTests { @Test @SuppressWarnings("unchecked") - public void extractMapSubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { + void extractMapSubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.b") .extractSubsection("{\"a\":{\"b\":{\"c\":5}}}".getBytes(), MediaType.APPLICATION_JSON); Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); @@ -52,8 +52,7 @@ public void extractMapSubsectionOfJsonMap() throws JsonParseException, JsonMappi @Test @SuppressWarnings("unchecked") - public void extractSingleElementArraySubsectionOfJsonMap() - throws JsonParseException, JsonMappingException, IOException { + void extractSingleElementArraySubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[]") .extractSubsection("{\"a\":[{\"b\":5}]}".getBytes(), MediaType.APPLICATION_JSON); Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); @@ -63,8 +62,7 @@ public void extractSingleElementArraySubsectionOfJsonMap() @Test @SuppressWarnings("unchecked") - public void extractMultiElementArraySubsectionOfJsonMap() - throws JsonParseException, JsonMappingException, IOException { + void extractMultiElementArraySubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a") .extractSubsection("{\"a\":[{\"b\":5},{\"b\":4}]}".getBytes(), MediaType.APPLICATION_JSON); Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); @@ -74,7 +72,7 @@ public void extractMultiElementArraySubsectionOfJsonMap() @Test @SuppressWarnings("unchecked") - public void extractMapSubsectionFromSingleElementArrayInAJsonMap() + void extractMapSubsectionFromSingleElementArrayInAJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b") .extractSubsection("{\"a\":[{\"b\":{\"c\":5}}]}".getBytes(), MediaType.APPLICATION_JSON); @@ -85,7 +83,7 @@ public void extractMapSubsectionFromSingleElementArrayInAJsonMap() @Test @SuppressWarnings("unchecked") - public void extractMapSubsectionWithCommonStructureFromMultiElementArrayInAJsonMap() + void extractMapSubsectionWithCommonStructureFromMultiElementArrayInAJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b") .extractSubsection("{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6}}]}".getBytes(), MediaType.APPLICATION_JSON); @@ -95,7 +93,7 @@ public void extractMapSubsectionWithCommonStructureFromMultiElementArrayInAJsonM } @Test - public void extractMapSubsectionWithVaryingStructureFromMultiElementArrayInAJsonMap() { + void extractMapSubsectionWithVaryingStructureFromMultiElementArrayInAJsonMap() { assertThatExceptionOfType(PayloadHandlingException.class) .isThrownBy(() -> new FieldPathPayloadSubsectionExtractor("a.[].b").extractSubsection( "{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6, \"d\": 7}}]}".getBytes(), MediaType.APPLICATION_JSON)) @@ -103,7 +101,7 @@ public void extractMapSubsectionWithVaryingStructureFromMultiElementArrayInAJson } @Test - public void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMap() { + void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMap() { assertThatExceptionOfType(PayloadHandlingException.class) .isThrownBy(() -> new FieldPathPayloadSubsectionExtractor("*.d").extractSubsection( "{\"a\":{\"b\":1},\"c\":{\"d\":{\"e\":1,\"f\":2}}}".getBytes(), MediaType.APPLICATION_JSON)) @@ -111,7 +109,7 @@ public void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMap() { } @Test - public void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMapWhereAllSubsectionFieldsAreOptional() { + void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMapWhereAllSubsectionFieldsAreOptional() { assertThatExceptionOfType(PayloadHandlingException.class) .isThrownBy(() -> new FieldPathPayloadSubsectionExtractor("*.d").extractSubsection( "{\"a\":{\"b\":1},\"c\":{\"d\":{\"e\":1,\"f\":2}}}".getBytes(), MediaType.APPLICATION_JSON, @@ -121,7 +119,7 @@ public void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMapWhere @Test @SuppressWarnings("unchecked") - public void extractMapSubsectionWithVaryingStructureDueToOptionalFieldsFromMultiElementArrayInAJsonMap() + void extractMapSubsectionWithVaryingStructureDueToOptionalFieldsFromMultiElementArrayInAJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b").extractSubsection( "{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6, \"d\": 7}}]}".getBytes(), MediaType.APPLICATION_JSON, @@ -133,7 +131,7 @@ public void extractMapSubsectionWithVaryingStructureDueToOptionalFieldsFromMulti @Test @SuppressWarnings("unchecked") - public void extractMapSubsectionWithVaryingStructureDueToOptionalParentFieldsFromMultiElementArrayInAJsonMap() + void extractMapSubsectionWithVaryingStructureDueToOptionalParentFieldsFromMultiElementArrayInAJsonMap() throws JsonParseException, JsonMappingException, IOException { byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b").extractSubsection( "{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6, \"d\": { \"e\": 7}}}]}".getBytes(), @@ -144,7 +142,7 @@ public void extractMapSubsectionWithVaryingStructureDueToOptionalParentFieldsFro } @Test - public void extractedSubsectionIsPrettyPrintedWhenInputIsPrettyPrinted() + void extractedSubsectionIsPrettyPrintedWhenInputIsPrettyPrinted() throws JsonParseException, JsonMappingException, JsonProcessingException, IOException { ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); byte[] prettyPrintedPayload = objectMapper @@ -157,7 +155,7 @@ public void extractedSubsectionIsPrettyPrintedWhenInputIsPrettyPrinted() } @Test - public void extractedSubsectionIsNotPrettyPrintedWhenInputIsNotPrettyPrinted() + void extractedSubsectionIsNotPrettyPrintedWhenInputIsNotPrettyPrinted() throws JsonParseException, JsonMappingException, JsonProcessingException, IOException { ObjectMapper objectMapper = new ObjectMapper(); byte[] payload = objectMapper @@ -169,7 +167,7 @@ public void extractedSubsectionIsNotPrettyPrintedWhenInputIsNotPrettyPrinted() } @Test - public void extractNonExistentSubsection() { + void extractNonExistentSubsection() { assertThatThrownBy(() -> new FieldPathPayloadSubsectionExtractor("a.c") .extractSubsection("{\"a\":{\"b\":{\"c\":5}}}".getBytes(), MediaType.APPLICATION_JSON)) .isInstanceOf(PayloadHandlingException.class) @@ -177,7 +175,7 @@ public void extractNonExistentSubsection() { } @Test - public void extractEmptyArraySubsection() { + void extractEmptyArraySubsection() { assertThatThrownBy(() -> new FieldPathPayloadSubsectionExtractor("a") .extractSubsection("{\"a\":[]}}".getBytes(), MediaType.APPLICATION_JSON)) .isInstanceOf(PayloadHandlingException.class) diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java index daadf9d4..da5b1e90 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,7 +18,7 @@ import java.util.Collections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java index 7f069fa1..43f0100a 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -31,10 +31,10 @@ * @author Andy Wilkinson * @author Mathias Düsterhöft */ -public class JsonContentHandlerTests { +class JsonContentHandlerTests { @Test - public void typeForFieldWithNullValueMustMatch() { + void typeForFieldWithNullValueMustMatch() { FieldDescriptor descriptor = new FieldDescriptor("a").type(JsonFieldType.STRING); assertThatExceptionOfType(FieldTypesDoNotMatchException.class) .isThrownBy(() -> new JsonContentHandler("{\"a\": null}".getBytes(), Arrays.asList(descriptor)) @@ -42,7 +42,7 @@ public void typeForFieldWithNullValueMustMatch() { } @Test - public void typeForFieldWithNotNullAndThenNullValueMustMatch() { + void typeForFieldWithNotNullAndThenNullValueMustMatch() { FieldDescriptor descriptor = new FieldDescriptor("a[].id").type(JsonFieldType.STRING); assertThatExceptionOfType(FieldTypesDoNotMatchException.class).isThrownBy( () -> new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes(), Arrays.asList(descriptor)) @@ -50,7 +50,7 @@ public void typeForFieldWithNotNullAndThenNullValueMustMatch() { } @Test - public void typeForFieldWithNullAndThenNotNullValueMustMatch() { + void typeForFieldWithNullAndThenNotNullValueMustMatch() { FieldDescriptor descriptor = new FieldDescriptor("a.[].id").type(JsonFieldType.STRING); assertThatExceptionOfType(FieldTypesDoNotMatchException.class).isThrownBy( () -> new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), Arrays.asList(descriptor)) @@ -58,7 +58,7 @@ public void typeForFieldWithNullAndThenNotNullValueMustMatch() { } @Test - public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() { + void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() { FieldDescriptor descriptor = new FieldDescriptor("a[].id").optional(); Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes(), Arrays.asList(descriptor)) @@ -67,7 +67,7 @@ public void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() { } @Test - public void typeForOptionalFieldWithNullAndThenNumberIsNumber() { + void typeForOptionalFieldWithNullAndThenNumberIsNumber() { FieldDescriptor descriptor = new FieldDescriptor("a[].id").optional(); Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), Arrays.asList(descriptor)) @@ -76,7 +76,7 @@ public void typeForOptionalFieldWithNullAndThenNumberIsNumber() { } @Test - public void typeForFieldWithNumberAndThenNullValueIsVaries() { + void typeForFieldWithNumberAndThenNullValueIsVaries() { FieldDescriptor descriptor = new FieldDescriptor("a[].id"); Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes(), Arrays.asList(descriptor)) @@ -85,7 +85,7 @@ public void typeForFieldWithNumberAndThenNullValueIsVaries() { } @Test - public void typeForFieldWithNullAndThenNumberIsVaries() { + void typeForFieldWithNullAndThenNumberIsVaries() { FieldDescriptor descriptor = new FieldDescriptor("a[].id"); Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), Arrays.asList(descriptor)) @@ -94,7 +94,7 @@ public void typeForFieldWithNullAndThenNumberIsVaries() { } @Test - public void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() { + void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() { FieldDescriptor descriptor = new FieldDescriptor("a").type(JsonFieldType.STRING).optional(); Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes(), Arrays.asList(descriptor)) .resolveFieldType(descriptor); @@ -102,7 +102,7 @@ public void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() { } @Test - public void typeForFieldWithSometimesPresentOptionalAncestorCanBeProvidedExplicitly() { + void typeForFieldWithSometimesPresentOptionalAncestorCanBeProvidedExplicitly() { FieldDescriptor descriptor = new FieldDescriptor("a.[].b.c").type(JsonFieldType.NUMBER); FieldDescriptor ancestor = new FieldDescriptor("a.[].b").optional(); Object fieldType = new JsonContentHandler("{\"a\":[ { \"d\": 4}, {\"b\":{\"c\":5}, \"d\": 4}]}".getBytes(), @@ -112,13 +112,13 @@ public void typeForFieldWithSometimesPresentOptionalAncestorCanBeProvidedExplici } @Test - public void failsFastWithNonJsonContent() { + void failsFastWithNonJsonContent() { assertThatExceptionOfType(PayloadHandlingException.class) .isThrownBy(() -> new JsonContentHandler("Non-JSON content".getBytes(), Collections.emptyList())); } @Test - public void describedFieldThatIsNotPresentIsConsideredMissing() { + void describedFieldThatIsNotPresentIsConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"), new FieldDescriptor("c")); List missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes(), @@ -129,7 +129,7 @@ public void describedFieldThatIsNotPresentIsConsideredMissing() { } @Test - public void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() { + void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"), new FieldDescriptor("c").optional()); List missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes(), @@ -139,7 +139,7 @@ public void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() { } @Test - public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresentIsConsideredMissing() { + void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresentIsConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"), new FieldDescriptor("a.c")); List missingFields = new JsonContentHandler("{\"a\":\"alpha\",\"b\":\"bravo\"}".getBytes(), @@ -150,7 +150,7 @@ public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresen } @Test - public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPresentIsNotConsideredMissing() { + void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPresentIsNotConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"), new FieldDescriptor("a.c")); List missingFields = new JsonContentHandler("{\"b\":\"bravo\"}".getBytes(), descriptors) @@ -159,7 +159,7 @@ public void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPre } @Test - public void describedFieldThatIsNotPresentNestedBeneathOptionalArrayThatIsEmptyIsNotConsideredMissing() { + void describedFieldThatIsNotPresentNestedBeneathOptionalArrayThatIsEmptyIsNotConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("outer"), new FieldDescriptor("outer[]").optional(), new FieldDescriptor("outer[].inner")); List missingFields = new JsonContentHandler("{\"outer\":[]}".getBytes(), descriptors) @@ -168,7 +168,7 @@ public void describedFieldThatIsNotPresentNestedBeneathOptionalArrayThatIsEmptyI } @Test - public void describedSometimesPresentFieldThatIsChildOfSometimesPresentOptionalArrayIsNotConsideredMissing() { + void describedSometimesPresentFieldThatIsChildOfSometimesPresentOptionalArrayIsNotConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a.[].c").optional(), new FieldDescriptor("a.[].c.d")); List missingFields = new JsonContentHandler( @@ -178,7 +178,7 @@ public void describedSometimesPresentFieldThatIsChildOfSometimesPresentOptionalA } @Test - public void describedMissingFieldThatIsChildOfNestedOptionalArrayThatIsEmptyIsNotConsideredMissing() { + void describedMissingFieldThatIsChildOfNestedOptionalArrayThatIsEmptyIsNotConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a.[].b").optional(), new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c")); List missingFields = new JsonContentHandler("{\"a\":[{\"b\":[]}]}".getBytes(), descriptors) @@ -187,7 +187,7 @@ public void describedMissingFieldThatIsChildOfNestedOptionalArrayThatIsEmptyIsNo } @Test - public void describedMissingFieldThatIsChildOfNestedOptionalArrayThatContainsAnObjectIsConsideredMissing() { + void describedMissingFieldThatIsChildOfNestedOptionalArrayThatContainsAnObjectIsConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a.[].b").optional(), new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c")); List missingFields = new JsonContentHandler("{\"a\":[{\"b\":[{}]}]}".getBytes(), descriptors) @@ -197,7 +197,7 @@ public void describedMissingFieldThatIsChildOfNestedOptionalArrayThatContainsAnO } @Test - public void describedMissingFieldThatIsChildOfOptionalObjectThatIsNullIsNotConsideredMissing() { + void describedMissingFieldThatIsChildOfOptionalObjectThatIsNullIsNotConsideredMissing() { List descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("a.b")); List missingFields = new JsonContentHandler("{\"a\":null}".getBytes(), descriptors) diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java index b350235d..69324c65 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -16,7 +16,7 @@ package org.springframework.restdocs.payload; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.payload.JsonFieldPath.PathType; @@ -28,131 +28,131 @@ * @author Andy Wilkinson * @author Jeremy Rickard */ -public class JsonFieldPathTests { +class JsonFieldPathTests { @Test - public void pathTypeOfSingleFieldIsSingle() { + void pathTypeOfSingleFieldIsSingle() { JsonFieldPath path = JsonFieldPath.compile("a"); assertThat(path.getType()).isEqualTo(PathType.SINGLE); } @Test - public void pathTypeOfSingleNestedFieldIsSingle() { + void pathTypeOfSingleNestedFieldIsSingle() { JsonFieldPath path = JsonFieldPath.compile("a.b"); assertThat(path.getType()).isEqualTo(PathType.SINGLE); } @Test - public void pathTypeOfTopLevelArrayIsSingle() { + void pathTypeOfTopLevelArrayIsSingle() { JsonFieldPath path = JsonFieldPath.compile("[]"); assertThat(path.getType()).isEqualTo(PathType.SINGLE); } @Test - public void pathTypeOfFieldBeneathTopLevelArrayIsMulti() { + void pathTypeOfFieldBeneathTopLevelArrayIsMulti() { JsonFieldPath path = JsonFieldPath.compile("[]a"); assertThat(path.getType()).isEqualTo(PathType.MULTI); } @Test - public void pathTypeOfSingleNestedArrayIsSingle() { + void pathTypeOfSingleNestedArrayIsSingle() { JsonFieldPath path = JsonFieldPath.compile("a[]"); assertThat(path.getType()).isEqualTo(PathType.SINGLE); } @Test - public void pathTypeOfArrayBeneathNestedFieldsIsSingle() { + void pathTypeOfArrayBeneathNestedFieldsIsSingle() { JsonFieldPath path = JsonFieldPath.compile("a.b[]"); assertThat(path.getType()).isEqualTo(PathType.SINGLE); } @Test - public void pathTypeOfArrayOfArraysIsMulti() { + void pathTypeOfArrayOfArraysIsMulti() { JsonFieldPath path = JsonFieldPath.compile("a[][]"); assertThat(path.getType()).isEqualTo(PathType.MULTI); } @Test - public void pathTypeOfFieldBeneathAnArrayIsMulti() { + void pathTypeOfFieldBeneathAnArrayIsMulti() { JsonFieldPath path = JsonFieldPath.compile("a[].b"); assertThat(path.getType()).isEqualTo(PathType.MULTI); } @Test - public void pathTypeOfFieldBeneathTopLevelWildcardIsMulti() { + void pathTypeOfFieldBeneathTopLevelWildcardIsMulti() { JsonFieldPath path = JsonFieldPath.compile("*.a"); assertThat(path.getType()).isEqualTo(PathType.MULTI); } @Test - public void pathTypeOfFieldBeneathNestedWildcardIsMulti() { + void pathTypeOfFieldBeneathNestedWildcardIsMulti() { JsonFieldPath path = JsonFieldPath.compile("a.*.b"); assertThat(path.getType()).isEqualTo(PathType.MULTI); } @Test - public void pathTypeOfLeafWidlcardIsMulti() { + void pathTypeOfLeafWidlcardIsMulti() { JsonFieldPath path = JsonFieldPath.compile("a.*"); assertThat(path.getType()).isEqualTo(PathType.MULTI); } @Test - public void compilationOfSingleElementPath() { + void compilationOfSingleElementPath() { assertThat(JsonFieldPath.compile("a").getSegments()).containsExactly("a"); } @Test - public void compilationOfMultipleElementPath() { + void compilationOfMultipleElementPath() { assertThat(JsonFieldPath.compile("a.b.c").getSegments()).containsExactly("a", "b", "c"); } @Test - public void compilationOfPathWithArraysWithNoDotSeparators() { + void compilationOfPathWithArraysWithNoDotSeparators() { assertThat(JsonFieldPath.compile("a[]b[]c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); } @Test - public void compilationOfPathWithArraysWithPreAndPostDotSeparators() { + void compilationOfPathWithArraysWithPreAndPostDotSeparators() { assertThat(JsonFieldPath.compile("a.[].b.[].c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); } @Test - public void compilationOfPathWithArraysWithPreDotSeparators() { + void compilationOfPathWithArraysWithPreDotSeparators() { assertThat(JsonFieldPath.compile("a.[]b.[]c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); } @Test - public void compilationOfPathWithArraysWithPostDotSeparators() { + void compilationOfPathWithArraysWithPostDotSeparators() { assertThat(JsonFieldPath.compile("a[].b[].c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); } @Test - public void compilationOfPathStartingWithAnArray() { + void compilationOfPathStartingWithAnArray() { assertThat(JsonFieldPath.compile("[]a.b.c").getSegments()).containsExactly("[]", "a", "b", "c"); } @Test - public void compilationOfMultipleElementPathWithBrackets() { + void compilationOfMultipleElementPathWithBrackets() { assertThat(JsonFieldPath.compile("['a']['b']['c']").getSegments()).containsExactly("a", "b", "c"); } @Test - public void compilationOfMultipleElementPathWithAndWithoutBrackets() { + void compilationOfMultipleElementPathWithAndWithoutBrackets() { assertThat(JsonFieldPath.compile("['a'][].b['c']").getSegments()).containsExactly("a", "[]", "b", "c"); } @Test - public void compilationOfMultipleElementPathWithAndWithoutBracketsAndEmbeddedDots() { + void compilationOfMultipleElementPathWithAndWithoutBracketsAndEmbeddedDots() { assertThat(JsonFieldPath.compile("['a.key'][].b['c']").getSegments()).containsExactly("a.key", "[]", "b", "c"); } @Test - public void compilationOfPathWithAWildcard() { + void compilationOfPathWithAWildcard() { assertThat(JsonFieldPath.compile("a.b.*.c").getSegments()).containsExactly("a", "b", "*", "c"); } @Test - public void compilationOfPathWithAWildcardInBrackets() { + void compilationOfPathWithAWildcardInBrackets() { assertThat(JsonFieldPath.compile("a.b.['*'].c").getSegments()).containsExactly("a", "b", "*", "c"); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java index 10b8d9a9..2e770649 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,7 +20,7 @@ import java.util.Arrays; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; @@ -31,10 +31,10 @@ * * @author Andy Wilkinson */ -public class JsonFieldPathsTests { +class JsonFieldPathsTests { @Test - public void noUncommonPathsForSingleItem() { + void noUncommonPathsForSingleItem() { assertThat( JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": [ { \"c\": 2}, {\"c\": 3}, {\"c\": null}]}"))) .getUncommon()) @@ -42,20 +42,20 @@ public void noUncommonPathsForSingleItem() { } @Test - public void noUncommonPathsForMultipleIdenticalItems() { + void noUncommonPathsForMultipleIdenticalItems() { Object item = json("{\"a\": 1, \"b\": [ { \"c\": 2}, {\"c\": 3} ]}"); assertThat(JsonFieldPaths.from(Arrays.asList(item, item)).getUncommon()).isEmpty(); } @Test - public void noUncommonPathsForMultipleMatchingItemsWithDifferentScalarValues() { + void noUncommonPathsForMultipleMatchingItemsWithDifferentScalarValues() { assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": [ { \"c\": 2}, {\"c\": 3} ]}"), json("{\"a\": 4, \"b\": [ { \"c\": 5}, {\"c\": 6} ]}"))) .getUncommon()).isEmpty(); } @Test - public void missingEntryInMapIsIdentifiedAsUncommon() { + void missingEntryInMapIsIdentifiedAsUncommon() { assertThat( JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1}"), json("{\"a\": 1}"), json("{\"a\": 1, \"b\": 2}"))) .getUncommon()) @@ -63,21 +63,21 @@ public void missingEntryInMapIsIdentifiedAsUncommon() { } @Test - public void missingEntryInNestedMapIsIdentifiedAsUncommon() { + void missingEntryInNestedMapIsIdentifiedAsUncommon() { assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"c\": 1, \"d\": 2}}"))) .getUncommon()).containsExactly("b.d"); } @Test - public void missingEntriesInNestedMapAreIdentifiedAsUncommon() { + void missingEntriesInNestedMapAreIdentifiedAsUncommon() { assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"d\": 2}}"))) .getUncommon()).containsExactly("b.c", "b.d"); } @Test - public void absentItemFromFieldExtractionCausesAllPresentFieldsToBeIdentifiedAsUncommon() { + void absentItemFromFieldExtractionCausesAllPresentFieldsToBeIdentifiedAsUncommon() { assertThat(JsonFieldPaths .from(Arrays.asList(ExtractedField.ABSENT, json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"d\": 2}}"))) @@ -85,14 +85,14 @@ public void absentItemFromFieldExtractionCausesAllPresentFieldsToBeIdentifiedAsU } @Test - public void missingEntryBeneathArrayIsIdentifiedAsUncommon() { + void missingEntryBeneathArrayIsIdentifiedAsUncommon() { assertThat(JsonFieldPaths .from(Arrays.asList(json("[{\"b\": 1}]"), json("[{\"b\": 1}]"), json("[{\"b\": 1, \"c\": 2}]"))) .getUncommon()).containsExactly("[].c"); } @Test - public void missingEntryBeneathNestedArrayIsIdentifiedAsUncommon() { + void missingEntryBeneathNestedArrayIsIdentifiedAsUncommon() { assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": [{\"b\": 1}]}"), json("{\"a\": [{\"b\": 1}]}"), json("{\"a\": [{\"b\": 1, \"c\": 2}]}"))) .getUncommon()).containsExactly("a.[].c"); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java index 4c2c4551..b8709985 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -26,7 +26,7 @@ import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; @@ -37,19 +37,19 @@ * * @author Andy Wilkinson */ -public class JsonFieldProcessorTests { +class JsonFieldProcessorTests { private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor(); @Test - public void extractTopLevelMapEntry() { + void extractTopLevelMapEntry() { Map payload = new HashMap<>(); payload.put("a", "alpha"); assertThat(this.fieldProcessor.extract("a", payload).getValue()).isEqualTo("alpha"); } @Test - public void extractNestedMapEntry() { + void extractNestedMapEntry() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -58,7 +58,7 @@ public void extractNestedMapEntry() { } @Test - public void extractTopLevelArray() { + void extractTopLevelArray() { List> payload = new ArrayList<>(); Map bravo = new HashMap<>(); bravo.put("b", "bravo"); @@ -68,7 +68,7 @@ public void extractTopLevelArray() { } @Test - public void extractArray() { + void extractArray() { Map payload = new HashMap<>(); Map bravo = new HashMap<>(); bravo.put("b", "bravo"); @@ -78,7 +78,7 @@ public void extractArray() { } @Test - public void extractArrayContents() { + void extractArrayContents() { Map payload = new HashMap<>(); Map bravo = new HashMap<>(); bravo.put("b", "bravo"); @@ -88,7 +88,7 @@ public void extractArrayContents() { } @Test - public void extractFromItemsInArray() { + void extractFromItemsInArray() { Map payload = new HashMap<>(); Map entry = new HashMap<>(); entry.put("b", "bravo"); @@ -98,7 +98,7 @@ public void extractFromItemsInArray() { } @Test - public void extractOccasionallyAbsentFieldFromItemsInArray() { + void extractOccasionallyAbsentFieldFromItemsInArray() { Map payload = new HashMap<>(); Map entry = new HashMap<>(); entry.put("b", "bravo"); @@ -109,7 +109,7 @@ public void extractOccasionallyAbsentFieldFromItemsInArray() { } @Test - public void extractOccasionallyNullFieldFromItemsInArray() { + void extractOccasionallyNullFieldFromItemsInArray() { Map payload = new HashMap<>(); Map nonNullField = new HashMap<>(); nonNullField.put("b", "bravo"); @@ -121,7 +121,7 @@ public void extractOccasionallyNullFieldFromItemsInArray() { } @Test - public void extractNestedArray() { + void extractNestedArray() { Map payload = new HashMap<>(); Map entry1 = createEntry("id:1"); Map entry2 = createEntry("id:2"); @@ -133,7 +133,7 @@ public void extractNestedArray() { } @Test - public void extractFromItemsInNestedArray() { + void extractFromItemsInNestedArray() { Map payload = new HashMap<>(); Map entry1 = createEntry("id:1"); Map entry2 = createEntry("id:2"); @@ -144,7 +144,7 @@ public void extractFromItemsInNestedArray() { } @Test - public void extractArraysFromItemsInNestedArray() { + void extractArraysFromItemsInNestedArray() { Map payload = new HashMap<>(); Map entry1 = createEntry("ids", Arrays.asList(1, 2)); Map entry2 = createEntry("ids", Arrays.asList(3)); @@ -156,27 +156,27 @@ public void extractArraysFromItemsInNestedArray() { } @Test - public void nonExistentTopLevelField() { + void nonExistentTopLevelField() { assertThat(this.fieldProcessor.extract("a", Collections.emptyMap()).getValue()) .isEqualTo(ExtractedField.ABSENT); } @Test - public void nonExistentNestedField() { + void nonExistentNestedField() { HashMap payload = new HashMap<>(); - payload.put("a", new HashMap()); + payload.put("a", new HashMap<>()); assertThat(this.fieldProcessor.extract("a.b", payload).getValue()).isEqualTo(ExtractedField.ABSENT); } @Test - public void nonExistentNestedFieldWhenParentIsNotAMap() { + void nonExistentNestedFieldWhenParentIsNotAMap() { HashMap payload = new HashMap<>(); payload.put("a", 5); assertThat(this.fieldProcessor.extract("a.b", payload).getValue()).isEqualTo(ExtractedField.ABSENT); } @Test - public void nonExistentFieldWhenParentIsAnArray() { + void nonExistentFieldWhenParentIsAnArray() { HashMap payload = new HashMap<>(); HashMap alpha = new HashMap<>(); alpha.put("b", Arrays.asList(new HashMap())); @@ -185,20 +185,20 @@ public void nonExistentFieldWhenParentIsAnArray() { } @Test - public void nonExistentArrayField() { + void nonExistentArrayField() { HashMap payload = new HashMap<>(); assertThat(this.fieldProcessor.extract("a[]", payload).getValue()).isEqualTo(ExtractedField.ABSENT); } @Test - public void nonExistentArrayFieldAsTypeDoesNotMatch() { + void nonExistentArrayFieldAsTypeDoesNotMatch() { HashMap payload = new HashMap<>(); payload.put("a", 5); assertThat(this.fieldProcessor.extract("a[]", payload).getValue()).isEqualTo(ExtractedField.ABSENT); } @Test - public void nonExistentFieldBeneathAnArray() { + void nonExistentFieldBeneathAnArray() { HashMap payload = new HashMap<>(); HashMap alpha = new HashMap<>(); alpha.put("b", Arrays.asList(new HashMap())); @@ -208,7 +208,7 @@ public void nonExistentFieldBeneathAnArray() { } @Test - public void removeTopLevelMapEntry() { + void removeTopLevelMapEntry() { Map payload = new HashMap<>(); payload.put("a", "alpha"); this.fieldProcessor.remove("a", payload); @@ -216,7 +216,7 @@ public void removeTopLevelMapEntry() { } @Test - public void mapWithEntriesIsNotRemovedWhenNotAlsoRemovingDescendants() { + void mapWithEntriesIsNotRemovedWhenNotAlsoRemovingDescendants() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -226,7 +226,7 @@ public void mapWithEntriesIsNotRemovedWhenNotAlsoRemovingDescendants() { } @Test - public void removeSubsectionRemovesMapWithEntries() { + void removeSubsectionRemovesMapWithEntries() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -236,7 +236,7 @@ public void removeSubsectionRemovesMapWithEntries() { } @Test - public void removeNestedMapEntry() { + void removeNestedMapEntry() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -247,7 +247,7 @@ public void removeNestedMapEntry() { @SuppressWarnings("unchecked") @Test - public void removeItemsInArray() throws IOException { + void removeItemsInArray() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", Map.class); this.fieldProcessor.remove("a[].b", payload); @@ -256,7 +256,7 @@ public void removeItemsInArray() throws IOException { @SuppressWarnings("unchecked") @Test - public void removeItemsInNestedArray() throws IOException { + void removeItemsInNestedArray() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [[{\"id\":1},{\"id\":2}], [{\"id\":3}]]}", Map.class); this.fieldProcessor.remove("a[][].id", payload); @@ -265,7 +265,7 @@ public void removeItemsInNestedArray() throws IOException { @SuppressWarnings("unchecked") @Test - public void removeDoesNotRemoveArrayWithMapEntries() throws IOException { + void removeDoesNotRemoveArrayWithMapEntries() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", Map.class); this.fieldProcessor.remove("a[]", payload); @@ -274,7 +274,7 @@ public void removeDoesNotRemoveArrayWithMapEntries() throws IOException { @SuppressWarnings("unchecked") @Test - public void removeDoesNotRemoveArrayWithListEntries() throws IOException { + void removeDoesNotRemoveArrayWithListEntries() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [[2],[3]]}", Map.class); this.fieldProcessor.remove("a[]", payload); assertThat(payload.size()).isEqualTo(1); @@ -282,7 +282,7 @@ public void removeDoesNotRemoveArrayWithListEntries() throws IOException { @SuppressWarnings("unchecked") @Test - public void removeRemovesArrayWithOnlyScalarEntries() throws IOException { + void removeRemovesArrayWithOnlyScalarEntries() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [\"bravo\", \"charlie\"]}", Map.class); this.fieldProcessor.remove("a", payload); assertThat(payload.size()).isEqualTo(0); @@ -290,7 +290,7 @@ public void removeRemovesArrayWithOnlyScalarEntries() throws IOException { @SuppressWarnings("unchecked") @Test - public void removeSubsectionRemovesArrayWithMapEntries() throws IOException { + void removeSubsectionRemovesArrayWithMapEntries() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", Map.class); this.fieldProcessor.removeSubsection("a[]", payload); @@ -299,14 +299,14 @@ public void removeSubsectionRemovesArrayWithMapEntries() throws IOException { @SuppressWarnings("unchecked") @Test - public void removeSubsectionRemovesArrayWithListEntries() throws IOException { + void removeSubsectionRemovesArrayWithListEntries() throws IOException { Map payload = new ObjectMapper().readValue("{\"a\": [[2],[3]]}", Map.class); this.fieldProcessor.removeSubsection("a[]", payload); assertThat(payload.size()).isEqualTo(0); } @Test - public void extractNestedEntryWithDotInKeys() { + void extractNestedEntryWithDotInKeys() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a.key", alpha); @@ -316,7 +316,7 @@ public void extractNestedEntryWithDotInKeys() { @SuppressWarnings("unchecked") @Test - public void extractNestedEntriesUsingTopLevelWildcard() { + void extractNestedEntriesUsingTopLevelWildcard() { Map payload = new LinkedHashMap<>(); Map alpha = new LinkedHashMap<>(); payload.put("a", alpha); @@ -330,7 +330,7 @@ public void extractNestedEntriesUsingTopLevelWildcard() { @SuppressWarnings("unchecked") @Test - public void extractNestedEntriesUsingMidLevelWildcard() { + void extractNestedEntriesUsingMidLevelWildcard() { Map payload = new LinkedHashMap<>(); Map alpha = new LinkedHashMap<>(); payload.put("a", alpha); @@ -344,7 +344,7 @@ public void extractNestedEntriesUsingMidLevelWildcard() { @SuppressWarnings("unchecked") @Test - public void extractUsingLeafWildcardMatchingSingleItem() { + void extractUsingLeafWildcardMatchingSingleItem() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -357,7 +357,7 @@ public void extractUsingLeafWildcardMatchingSingleItem() { @SuppressWarnings("unchecked") @Test - public void extractUsingLeafWildcardMatchingMultipleItems() { + void extractUsingLeafWildcardMatchingMultipleItems() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -368,7 +368,7 @@ public void extractUsingLeafWildcardMatchingMultipleItems() { } @Test - public void removeUsingLeafWildcard() { + void removeUsingLeafWildcard() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -379,7 +379,7 @@ public void removeUsingLeafWildcard() { } @Test - public void removeUsingTopLevelWildcard() { + void removeUsingTopLevelWildcard() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -390,7 +390,7 @@ public void removeUsingTopLevelWildcard() { } @Test - public void removeUsingMidLevelWildcard() { + void removeUsingMidLevelWildcard() { Map payload = new LinkedHashMap<>(); Map alpha = new LinkedHashMap<>(); payload.put("a", alpha); @@ -407,28 +407,28 @@ public void removeUsingMidLevelWildcard() { } @Test - public void hasFieldIsTrueForNonNullFieldInMap() { + void hasFieldIsTrueForNonNullFieldInMap() { Map payload = new HashMap<>(); payload.put("a", "alpha"); assertThat(this.fieldProcessor.hasField("a", payload)).isTrue(); } @Test - public void hasFieldIsTrueForNullFieldInMap() { + void hasFieldIsTrueForNullFieldInMap() { Map payload = new HashMap<>(); payload.put("a", null); assertThat(this.fieldProcessor.hasField("a", payload)).isTrue(); } @Test - public void hasFieldIsFalseForAbsentFieldInMap() { + void hasFieldIsFalseForAbsentFieldInMap() { Map payload = new HashMap<>(); payload.put("a", null); assertThat(this.fieldProcessor.hasField("b", payload)).isFalse(); } @Test - public void hasFieldIsTrueForNeverNullFieldBeneathArray() { + void hasFieldIsTrueForNeverNullFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", "bravo"); @@ -437,7 +437,7 @@ public void hasFieldIsTrueForNeverNullFieldBeneathArray() { } @Test - public void hasFieldIsTrueForAlwaysNullFieldBeneathArray() { + void hasFieldIsTrueForAlwaysNullFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", null); @@ -446,7 +446,7 @@ public void hasFieldIsTrueForAlwaysNullFieldBeneathArray() { } @Test - public void hasFieldIsFalseForAlwaysAbsentFieldBeneathArray() { + void hasFieldIsFalseForAlwaysAbsentFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", "bravo"); @@ -455,7 +455,7 @@ public void hasFieldIsFalseForAlwaysAbsentFieldBeneathArray() { } @Test - public void hasFieldIsFalseForOccasionallyAbsentFieldBeneathArray() { + void hasFieldIsFalseForOccasionallyAbsentFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", "bravo"); @@ -464,7 +464,7 @@ public void hasFieldIsFalseForOccasionallyAbsentFieldBeneathArray() { } @Test - public void hasFieldIsFalseForOccasionallyNullFieldBeneathArray() { + void hasFieldIsFalseForOccasionallyNullFieldBeneathArray() { Map payload = new HashMap<>(); Map fieldPresent = new HashMap<>(); fieldPresent.put("b", "bravo"); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java index e9bf48d8..4c469005 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.io.IOException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -29,148 +29,148 @@ * * @author Andy Wilkinson */ -public class JsonFieldTypesDiscovererTests { +class JsonFieldTypesDiscovererTests { private final JsonFieldTypesDiscoverer fieldTypeDiscoverer = new JsonFieldTypesDiscoverer(); @Test - public void arrayField() throws IOException { + void arrayField() throws IOException { assertThat(discoverFieldTypes("[]")).containsExactly(JsonFieldType.ARRAY); } @Test - public void topLevelArray() throws IOException { + void topLevelArray() throws IOException { assertThat(discoverFieldTypes("[]", "[{\"a\":\"alpha\"}]")).containsExactly(JsonFieldType.ARRAY); } @Test - public void nestedArray() throws IOException { + void nestedArray() throws IOException { assertThat(discoverFieldTypes("a[]", "{\"a\": [{\"b\":\"bravo\"}]}")).containsExactly(JsonFieldType.ARRAY); } @Test - public void arrayNestedBeneathAnArray() throws IOException { + void arrayNestedBeneathAnArray() throws IOException { assertThat(discoverFieldTypes("a[].b[]", "{\"a\": [{\"b\": [ 1, 2 ]}]}")).containsExactly(JsonFieldType.ARRAY); } @Test - public void specificFieldOfObjectInArrayNestedBeneathAnArray() throws IOException { + void specificFieldOfObjectInArrayNestedBeneathAnArray() throws IOException { assertThat(discoverFieldTypes("a[].b[].c", "{\"a\": [{\"b\": [ {\"c\": 5}, {\"c\": 5}]}]}")) .containsExactly(JsonFieldType.NUMBER); } @Test - public void booleanField() throws IOException { + void booleanField() throws IOException { assertThat(discoverFieldTypes("true")).containsExactly(JsonFieldType.BOOLEAN); } @Test - public void objectField() throws IOException { + void objectField() throws IOException { assertThat(discoverFieldTypes("{}")).containsExactly(JsonFieldType.OBJECT); } @Test - public void nullField() throws IOException { + void nullField() throws IOException { assertThat(discoverFieldTypes("null")).containsExactly(JsonFieldType.NULL); } @Test - public void numberField() throws IOException { + void numberField() throws IOException { assertThat(discoverFieldTypes("1.2345")).containsExactly(JsonFieldType.NUMBER); } @Test - public void stringField() throws IOException { + void stringField() throws IOException { assertThat(discoverFieldTypes("\"Foo\"")).containsExactly(JsonFieldType.STRING); } @Test - public void nestedField() throws IOException { + void nestedField() throws IOException { assertThat(discoverFieldTypes("a.b.c", "{\"a\":{\"b\":{\"c\":{}}}}")).containsExactly(JsonFieldType.OBJECT); } @Test - public void multipleFieldsWithSameType() throws IOException { + void multipleFieldsWithSameType() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":2}]}")) .containsExactly(JsonFieldType.NUMBER); } @Test - public void multipleFieldsWithDifferentTypes() throws IOException { + void multipleFieldsWithDifferentTypes() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":true}]}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.BOOLEAN); } @Test - public void multipleFieldsWithDifferentTypesAndSometimesAbsent() throws IOException { + void multipleFieldsWithDifferentTypesAndSometimesAbsent() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":true}, {}]}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.BOOLEAN, JsonFieldType.NULL); } @Test - public void multipleFieldsWhenSometimesAbsent() throws IOException { + void multipleFieldsWhenSometimesAbsent() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":2}, {}]}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.NULL); } @Test - public void multipleFieldsWhenSometimesNull() throws IOException { + void multipleFieldsWhenSometimesNull() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":2}, {\"id\":null}]}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.NULL); } @Test - public void multipleFieldsWithDifferentTypesAndSometimesNull() throws IOException { + void multipleFieldsWithDifferentTypesAndSometimesNull() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":true}, {\"id\":null}]}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.BOOLEAN, JsonFieldType.NULL); } @Test - public void multipleFieldsWhenEitherNullOrAbsent() throws IOException { + void multipleFieldsWhenEitherNullOrAbsent() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{},{\"id\":null}]}")) .containsExactlyInAnyOrder(JsonFieldType.NULL); } @Test - public void multipleFieldsThatAreAllNull() throws IOException { + void multipleFieldsThatAreAllNull() throws IOException { assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":null},{\"id\":null}]}")) .containsExactlyInAnyOrder(JsonFieldType.NULL); } @Test - public void nonExistentSingleFieldProducesFieldDoesNotExistException() { + void nonExistentSingleFieldProducesFieldDoesNotExistException() { assertThatExceptionOfType(FieldDoesNotExistException.class) .isThrownBy(() -> discoverFieldTypes("a.b", "{\"a\":{}}")) .withMessage("The payload does not contain a field with the path 'a.b'"); } @Test - public void nonExistentMultipleFieldsProducesFieldDoesNotExistException() { + void nonExistentMultipleFieldsProducesFieldDoesNotExistException() { assertThatExceptionOfType(FieldDoesNotExistException.class) .isThrownBy(() -> discoverFieldTypes("a[].b", "{\"a\":[{\"c\":1},{\"c\":2}]}")) .withMessage("The payload does not contain a field with the path 'a[].b'"); } @Test - public void leafWildcardWithCommonType() throws IOException { + void leafWildcardWithCommonType() throws IOException { assertThat(discoverFieldTypes("a.*", "{\"a\": {\"b\": 5, \"c\": 6}}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER); } @Test - public void leafWildcardWithVaryingType() throws IOException { + void leafWildcardWithVaryingType() throws IOException { assertThat(discoverFieldTypes("a.*", "{\"a\": {\"b\": 5, \"c\": \"six\"}}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.STRING); } @Test - public void intermediateWildcardWithCommonType() throws IOException { + void intermediateWildcardWithCommonType() throws IOException { assertThat(discoverFieldTypes("a.*.d", "{\"a\": {\"b\": {\"d\": 4}, \"c\": {\"d\": 5}}}}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER); } @Test - public void intermediateWildcardWithVaryingType() throws IOException { + void intermediateWildcardWithVaryingType() throws IOException { assertThat(discoverFieldTypes("a.*.d", "{\"a\": {\"b\": {\"d\": 4}, \"c\": {\"d\": \"four\"}}}}")) .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.STRING); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java index 0828262f..1d2a869d 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,7 +18,7 @@ import java.util.EnumSet; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -27,32 +27,32 @@ * * @author Andy Wilkinson */ -public class JsonFieldTypesTests { +class JsonFieldTypesTests { @Test - public void singleTypeCoalescesToThatType() { + void singleTypeCoalescesToThatType() { assertThat(new JsonFieldTypes(JsonFieldType.NUMBER).coalesce(false)).isEqualTo(JsonFieldType.NUMBER); } @Test - public void singleTypeCoalescesToThatTypeWhenOptional() { + void singleTypeCoalescesToThatTypeWhenOptional() { assertThat(new JsonFieldTypes(JsonFieldType.NUMBER).coalesce(true)).isEqualTo(JsonFieldType.NUMBER); } @Test - public void multipleTypesCoalescesToVaries() { + void multipleTypesCoalescesToVaries() { assertThat(new JsonFieldTypes(EnumSet.of(JsonFieldType.ARRAY, JsonFieldType.NUMBER)).coalesce(false)) .isEqualTo(JsonFieldType.VARIES); } @Test - public void nullAndNonNullTypesCoalescesToVaries() { + void nullAndNonNullTypesCoalescesToVaries() { assertThat(new JsonFieldTypes(EnumSet.of(JsonFieldType.ARRAY, JsonFieldType.NULL)).coalesce(false)) .isEqualTo(JsonFieldType.VARIES); } @Test - public void nullAndNonNullTypesCoalescesToNonNullTypeWhenOptional() { + void nullAndNonNullTypesCoalescesToNonNullTypeWhenOptional() { assertThat(new JsonFieldTypes(EnumSet.of(JsonFieldType.ARRAY, JsonFieldType.NULL)).coalesce(true)) .isEqualTo(JsonFieldType.ARRAY); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java index 7c57c3c1..11b6df5f 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.restdocs.payload.PayloadDocumentation.applyPathPrefix; @@ -31,10 +31,10 @@ * * @author Andy Wilkinson */ -public class PayloadDocumentationTests { +class PayloadDocumentationTests { @Test - public void applyPathPrefixAppliesPrefixToDescriptorPaths() { + void applyPathPrefixAppliesPrefixToDescriptorPaths() { List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo"), fieldWithPath("charlie"))); assertThat(descriptors.size()).isEqualTo(2); @@ -42,21 +42,21 @@ public void applyPathPrefixAppliesPrefixToDescriptorPaths() { } @Test - public void applyPathPrefixCopiesIgnored() { + void applyPathPrefixCopiesIgnored() { List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").ignored())); assertThat(descriptors.size()).isEqualTo(1); assertThat(descriptors.get(0).isIgnored()).isTrue(); } @Test - public void applyPathPrefixCopiesOptional() { + void applyPathPrefixCopiesOptional() { List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").optional())); assertThat(descriptors.size()).isEqualTo(1); assertThat(descriptors.get(0).isOptional()).isTrue(); } @Test - public void applyPathPrefixCopiesDescription() { + void applyPathPrefixCopiesDescription() { List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").description("Some field"))); assertThat(descriptors.size()).isEqualTo(1); @@ -64,7 +64,7 @@ public void applyPathPrefixCopiesDescription() { } @Test - public void applyPathPrefixCopiesType() { + void applyPathPrefixCopiesType() { List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").type(JsonFieldType.OBJECT))); assertThat(descriptors.size()).isEqualTo(1); @@ -72,7 +72,7 @@ public void applyPathPrefixCopiesType() { } @Test - public void applyPathPrefixCopiesAttributes() { + void applyPathPrefixCopiesAttributes() { List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").attributes(key("a").value("alpha"), key("b").value("bravo")))); assertThat(descriptors.size()).isEqualTo(1); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java index 67e25f57..f9c94dbd 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,14 @@ import java.io.IOException; -import org.junit.Test; - import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody; import static org.springframework.restdocs.snippet.Attributes.attributes; @@ -41,89 +36,84 @@ * * @author Andy Wilkinson */ -public class RequestBodyPartSnippetTests extends AbstractSnippetTests { - - public RequestBodyPartSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class RequestBodyPartSnippetTests { - @Test - public void requestPartWithBody() throws IOException { + @RenderedSnippetTest + void requestPartWithBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { requestPartBody("one") - .document(this.operationBuilder.request("http://localhost/").part("one", "some content".getBytes()).build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock(null, "nowrap").withContent("some content")); + .document(operationBuilder.request("http://localhost/").part("one", "some content".getBytes()).build()); + assertThat(snippets.requestPartBody("one")) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("some content")); } - @Test - public void requestPartWithNoBody() throws IOException { - requestPartBody("one") - .document(this.operationBuilder.request("http://localhost/").part("one", new byte[0]).build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock(null, "nowrap").withContent("")); + @RenderedSnippetTest + void requestPartWithNoBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestPartBody("one").document(operationBuilder.request("http://localhost/").part("one", new byte[0]).build()); + assertThat(snippets.requestPartBody("one")) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("")); } - @Test - public void requestPartWithJsonMediaType() throws IOException { - requestPartBody("one").document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestPartWithJsonMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestPartBody("one").document(operationBuilder.request("http://localhost/") .part("one", "".getBytes()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock("json", "nowrap").withContent("")); + assertThat(snippets.requestPartBody("one")) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); } - @Test - public void requestPartWithJsonSubtypeMediaType() throws IOException { - requestPartBody("one").document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestPartWithJsonSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestPartBody("one").document(operationBuilder.request("http://localhost/") .part("one", "".getBytes()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock("json", "nowrap").withContent("")); + assertThat(snippets.requestPartBody("one")) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); } - @Test - public void requestPartWithXmlMediaType() throws IOException { - requestPartBody("one").document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestPartWithXmlMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestPartBody("one").document(operationBuilder.request("http://localhost/") .part("one", "".getBytes()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock("xml", "nowrap").withContent("")); + assertThat(snippets.requestPartBody("one")) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); } - @Test - public void requestPartWithXmlSubtypeMediaType() throws IOException { - requestPartBody("one").document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestPartWithXmlSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestPartBody("one").document(operationBuilder.request("http://localhost/") .part("one", "".getBytes()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock("xml", "nowrap").withContent("")); + assertThat(snippets.requestPartBody("one")) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); } - @Test - public void subsectionOfRequestPartBody() throws IOException { - requestPartBody("one", beneathPath("a.b")).document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void subsectionOfRequestPartBody(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestPartBody("one", beneathPath("a.b")).document(operationBuilder.request("http://localhost/") .part("one", "{\"a\":{\"b\":{\"c\":5}}}".getBytes()) .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body-beneath-a.b")) - .is(codeBlock(null, "nowrap").withContent("{\"c\":5}")); + assertThat(snippets.requestPartBody("one", "beneath-a.b")) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("{\"c\":5}")); } - @Test - public void customSnippetAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-part-body")) - .willReturn(snippetResource("request-part-body-with-language")); - requestPartBody("one", attributes(key("language").value("json"))).document( - this.operationBuilder.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .part("one", "{\"a\":\"alpha\"}".getBytes()) - .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-body")) - .is(codeBlock("json", "nowrap").withContent("{\"a\":\"alpha\"}")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-part-body", template = "request-part-body-with-language") + void customSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestPartBody("one", attributes(key("language").value("json"))) + .document(operationBuilder.request("http://localhost/").part("one", "{\"a\":\"alpha\"}".getBytes()).build()); + assertThat(snippets.requestPartBody("one")).isCodeBlock( + (codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("{\"a\":\"alpha\"}")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java index 47ab24b4..c332fdcc 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,14 @@ import java.io.IOException; -import org.junit.Test; - import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestBody; import static org.springframework.restdocs.snippet.Attributes.attributes; @@ -41,77 +36,74 @@ * * @author Andy Wilkinson */ -public class RequestBodySnippetTests extends AbstractSnippetTests { - - public RequestBodySnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class RequestBodySnippetTests { - @Test - public void requestWithBody() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost/").content("some content").build()); - assertThat(this.generatedSnippets.snippet("request-body")) - .is(codeBlock(null, "nowrap").withContent("some content")); + @RenderedSnippetTest + void requestWithBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestBody().document(operationBuilder.request("http://localhost/").content("some content").build()); + assertThat(snippets.requestBody()) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("some content")); } - @Test - public void requestWithNoBody() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost/").build()); - assertThat(this.generatedSnippets.snippet("request-body")).is(codeBlock(null, "nowrap").withContent("")); + @RenderedSnippetTest + void requestWithNoBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestBody().document(operationBuilder.request("http://localhost/").build()); + assertThat(snippets.requestBody()).isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("")); } - @Test - public void requestWithJsonMediaType() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestWithJsonMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestBody().document(operationBuilder.request("http://localhost/") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-body")).is(codeBlock("json", "nowrap").withContent("")); + assertThat(snippets.requestBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); } - @Test - public void requestWithJsonSubtypeMediaType() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestWithJsonSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestBody().document(operationBuilder.request("http://localhost/") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-body")).is(codeBlock("json", "nowrap").withContent("")); + assertThat(snippets.requestBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); } - @Test - public void requestWithXmlMediaType() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestWithXmlMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestBody().document(operationBuilder.request("http://localhost/") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-body")).is(codeBlock("xml", "nowrap").withContent("")); + assertThat(snippets.requestBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); } - @Test - public void requestWithXmlSubtypeMediaType() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void requestWithXmlSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + requestBody().document(operationBuilder.request("http://localhost/") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("request-body")).is(codeBlock("xml", "nowrap").withContent("")); + assertThat(snippets.requestBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); } - @Test - public void subsectionOfRequestBody() throws IOException { + @RenderedSnippetTest + void subsectionOfRequestBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { requestBody(beneathPath("a.b")) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\":{\"b\":{\"c\":5}}}").build()); - assertThat(this.generatedSnippets.snippet("request-body-beneath-a.b")) - .is(codeBlock(null, "nowrap").withContent("{\"c\":5}")); + .document(operationBuilder.request("http://localhost/").content("{\"a\":{\"b\":{\"c\":5}}}").build()); + assertThat(snippets.requestBody("beneath-a.b")) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("{\"c\":5}")); } - @Test - public void customSnippetAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-body")) - .willReturn(snippetResource("request-body-with-language")); - requestBody(attributes(key("language").value("json"))).document( - this.operationBuilder.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .content("{\"a\":\"alpha\"}") - .build()); - assertThat(this.generatedSnippets.snippet("request-body")) - .is(codeBlock("json", "nowrap").withContent("{\"a\":\"alpha\"}")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-body", template = "request-body-with-language") + void customSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + requestBody(attributes(key("language").value("json"))) + .document(operationBuilder.request("http://localhost/").content("{\"a\":\"alpha\"}").build()); + assertThat(snippets.requestBody()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("{\"a\":\"alpha\"}")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java deleted file mode 100644 index 1eb5a5b9..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetFailureTests.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; - -/** - * Tests for failures when rendering {@link RequestFieldsSnippet} due to missing or - * undocumented fields. - * - * @author Andy Wilkinson - */ -public class RequestFieldsSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedRequestField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": 5}").build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @Test - public void missingRequestField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(this.operationBuilder.request("http://localhost/").content("{}").build())) - .withMessage("Fields with the following paths were not found in the payload: [a.b]"); - } - - @Test - public void missingOptionalRequestFieldWithNoTypeProvided() { - assertThatExceptionOfType(FieldTypeRequiredException.class).isThrownBy( - () -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").optional())) - .document(this.operationBuilder.request("http://localhost/").content("{ }").build())); - } - - @Test - public void undocumentedRequestFieldAndMissingRequestField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(this.operationBuilder.request("http://localhost/").content("{ \"a\": { \"c\": 5 }}").build())) - .withMessageStartingWith("The following parts of the payload were not documented:") - .withMessageEndingWith("Fields with the following paths were not found in the payload: [a.b]"); - } - - @Test - public void attemptToDocumentFieldsWithNoRequestBody() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").build())) - .withMessage("Cannot document request fields as the request body is empty"); - } - - @Test - public void fieldWithExplicitTypeThatDoesNotMatchThePayload() { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.OBJECT))) - .document(this.operationBuilder.request("http://localhost/").content("{ \"a\": 5 }").build())) - .withMessage("The documented type of the field 'a' is Object but the actual type is Number"); - } - - @Test - public void fieldWithExplicitSpecificTypeThatActuallyVaries() { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("[].a").description("one").type(JsonFieldType.OBJECT))) - .document(this.operationBuilder.request("http://localhost/") - .content("[{ \"a\": 5 },{ \"a\": \"b\" }]") - .build())) - .withMessage("The documented type of the field '[].a' is Object but the actual type is Varies"); - } - - @Test - public void undocumentedXmlRequestField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) - .document(this.operationBuilder.request("http://localhost/") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @Test - public void xmlDescendentsAreNotDocumentedByFieldDescriptor() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").type("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @Test - public void xmlRequestFieldWithNoType() { - assertThatExceptionOfType(FieldTypeRequiredException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())); - } - - @Test - public void missingXmlRequestField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a/b").description("one"), fieldWithPath("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/") - .content("") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage("Fields with the following paths were not found in the payload: [a/b]"); - } - - @Test - public void undocumentedXmlRequestFieldAndMissingXmlRequestField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) - .document(this.operationBuilder.request("http://localhost/") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:") - .withMessageEndingWith("Fields with the following paths were not found in the payload: [a/b]"); - } - - @Test - public void unsupportedContent() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) - .document(this.operationBuilder.request("http://localhost/") - .content("Some plain text") - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .build())) - .withMessage("Cannot handle text/plain content as it could not be parsed as JSON or XML"); - } - - @Test - public void nonOptionalFieldBeneathArrayThatIsSometimesNull() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.request("http://localhost/") - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"b\": null, \"c\": 2}," + " {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - - @Test - public void nonOptionalFieldBeneathArrayThatIsSometimesAbsent() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.request("http://localhost/") - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java index bb6ff7b4..7c6394d9 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -18,21 +18,19 @@ import java.io.IOException; import java.util.Arrays; - -import org.junit.Test; +import java.util.Collections; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; @@ -46,342 +44,492 @@ * @author Andy Wilkinson * @author Sungjun Lee */ -public class RequestFieldsSnippetTests extends AbstractSnippetTests { - - public RequestFieldsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class RequestFieldsSnippetTests { - @Test - public void mapRequestWithFields() throws IOException { + @RenderedSnippetTest + void mapRequestWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two"), fieldWithPath("a").description("three"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a.b`", "`Number`", "one") + .row("`a.c`", "`String`", "two") + .row("`a`", "`Object`", "three")); } - @Test - public void mapRequestWithNullField() throws IOException { + @RenderedSnippetTest + void mapRequestWithNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": {\"b\": null}}").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Null`", "one")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": {\"b\": null}}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`Null`", "one")); } - @Test - public void entireSubsectionsCanBeDocumented() throws IOException { + @RenderedSnippetTest + void entireSubsectionsCanBeDocumented(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(subsectionWithPath("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Object`", "one")); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Object`", "one")); } - @Test - public void subsectionOfMapRequest() throws IOException { + @RenderedSnippetTest + void subsectionOfMapRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { requestFields(beneathPath("a"), fieldWithPath("b").description("one"), fieldWithPath("c").description("two")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") .build()); - assertThat(this.generatedSnippets.snippet("request-fields-beneath-a")) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "one") + assertThat(snippets.requestFields("beneath-a")) + .isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`b`", "`Number`", "one") .row("`c`", "`String`", "two")); } - @Test - public void subsectionOfMapRequestWithCommonPrefix() throws IOException { + @RenderedSnippetTest + void subsectionOfMapRequestWithCommonPrefix(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { requestFields(beneathPath("a")).andWithPrefix("b.", fieldWithPath("c").description("two")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": {\"c\": \"charlie\"}}}") .build()); - assertThat(this.generatedSnippets.snippet("request-fields-beneath-a")) - .is(tableWithHeader("Path", "Type", "Description").row("`b.c`", "`String`", "two")); + assertThat(snippets.requestFields("beneath-a")) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b.c`", "`String`", "two")); } - @Test - public void arrayRequestWithFields() throws IOException { + @RenderedSnippetTest + void arrayRequestWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet( Arrays.asList(fieldWithPath("[]").description("one"), fieldWithPath("[]a.b").description("two"), fieldWithPath("[]a.c").description("three"), fieldWithPath("[]a").description("four"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("[{\"a\": {\"b\": 5, \"c\":\"charlie\"}}," + "{\"a\": {\"b\": 4, \"c\":\"chalk\"}}]") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`[]`", "`Array`", "one") - .row("`[]a.b`", "`Number`", "two") - .row("`[]a.c`", "`String`", "three") - .row("`[]a`", "`Object`", "four")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`[]`", "`Array`", "one") + .row("`[]a.b`", "`Number`", "two") + .row("`[]a.c`", "`String`", "three") + .row("`[]a`", "`Object`", "four")); } - @Test - public void arrayRequestWithAlwaysNullField() throws IOException { + @RenderedSnippetTest + void arrayRequestWithAlwaysNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("[{\"a\": {\"b\": null}}," + "{\"a\": {\"b\": null}}]") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`[]a.b`", "`Null`", "one")); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`[]a.b`", "`Null`", "one")); } - @Test - public void subsectionOfArrayRequest() throws IOException { + @RenderedSnippetTest + void subsectionOfArrayRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { requestFields(beneathPath("[].a"), fieldWithPath("b").description("one"), fieldWithPath("c").description("two")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("[{\"a\": {\"b\": 5, \"c\": \"charlie\"}}]") .build()); - assertThat(this.generatedSnippets.snippet("request-fields-beneath-[].a")) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "one") + assertThat(snippets.requestFields("beneath-[].a")) + .isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`b`", "`Number`", "one") .row("`c`", "`String`", "two")); } - @Test - public void ignoredRequestField() throws IOException { + @RenderedSnippetTest + void ignoredRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").ignored(), fieldWithPath("b").description("Field b"))) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": 5, \"b\": 4}").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": 5, \"b\": 4}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); } - @Test - public void entireSubsectionCanBeIgnored() throws IOException { + @RenderedSnippetTest + void entireSubsectionCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet( Arrays.asList(subsectionWithPath("a").ignored(), fieldWithPath("c").description("Field c"))) - .document( - this.operationBuilder.request("http://localhost/").content("{\"a\": {\"b\": 5}, \"c\": 4}").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`c`", "`Number`", "Field c")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": {\"b\": 5}, \"c\": 4}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`c`", "`Number`", "Field c")); } - @Test - public void allUndocumentedRequestFieldsCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedRequestFieldsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": 5, \"b\": 4}").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": 5, \"b\": 4}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); } - @Test - public void allUndocumentedFieldsContinueToBeIgnoredAfterAddingDescriptors() throws IOException { + @RenderedSnippetTest + void allUndocumentedFieldsContinueToBeIgnoredAfterAddingDescriptors(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) .andWithPrefix("c.", fieldWithPath("d").description("Field d")) - .document(this.operationBuilder.request("http://localhost/") - .content("{\"a\":5,\"b\":4,\"c\":{\"d\": 3}}") - .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b") - .row("`c.d`", "`Number`", "Field d")); + .document( + operationBuilder.request("http://localhost/").content("{\"a\":5,\"b\":4,\"c\":{\"d\": 3}}").build()); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`b`", "`Number`", "Field b") + .row("`c.d`", "`Number`", "Field d")); } - @Test - public void missingOptionalRequestField() throws IOException { + @RenderedSnippetTest + void missingOptionalRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet( Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(this.operationBuilder.request("http://localhost/").content("{}").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); + .document(operationBuilder.request("http://localhost/").content("{}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); } - @Test - public void missingIgnoredOptionalRequestFieldDoesNotRequireAType() throws IOException { + @RenderedSnippetTest + void missingIgnoredOptionalRequestFieldDoesNotRequireAType(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").ignored().optional())) - .document(this.operationBuilder.request("http://localhost/").content("{}").build()); - assertThat(this.generatedSnippets.requestFields()).is(tableWithHeader("Path", "Type", "Description")); + .document(operationBuilder.request("http://localhost/").content("{}").build()); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description")); } - @Test - public void presentOptionalRequestField() throws IOException { + @RenderedSnippetTest + void presentOptionalRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet( Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document( - this.operationBuilder.request("http://localhost/").content("{\"a\": { \"b\": \"bravo\"}}").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": { \"b\": \"bravo\"}}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); } - @Test - public void requestFieldsWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-fields")) - .willReturn(snippetResource("request-fields-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-fields", template = "request-fields-with-title") + void requestFieldsWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), attributes(key("title").value("Custom title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .content("{\"a\": \"foo\"}") - .build()); - assertThat(this.generatedSnippets.requestFields()).contains("Custom title"); + .document(operationBuilder.request("http://localhost/").content("{\"a\": \"foo\"}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withTitleAndHeader("Custom title", "Path", "Type", "Description") + .row("a", "String", "one")); } - @Test - public void requestFieldsWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-fields")) - .willReturn(snippetResource("request-fields-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-fields", template = "request-fields-with-extra-column") + void requestFieldsWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet( Arrays.asList(fieldWithPath("a.b").description("one").attributes(key("foo").value("alpha")), fieldWithPath("a.c").description("two").attributes(key("foo").value("bravo")), fieldWithPath("a").description("three").attributes(key("foo").value("charlie")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description", "Foo").row("a.b", "Number", "one", "alpha") - .row("a.c", "String", "two", "bravo") - .row("a", "Object", "three", "charlie")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description", "Foo") + .row("a.b", "Number", "one", "alpha") + .row("a.c", "String", "two", "bravo") + .row("a", "Object", "three", "charlie")); } - @Test - public void fieldWithExplicitExactlyMatchingType() throws IOException { + @RenderedSnippetTest + void fieldWithExplicitExactlyMatchingType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": 5 }").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Number`", "one")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": 5 }").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Number`", "one")); } - @Test - public void fieldWithExplicitVariesType() throws IOException { + @RenderedSnippetTest + void fieldWithExplicitVariesType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.VARIES))) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": 5 }").build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Varies`", "one")); + .document(operationBuilder.request("http://localhost/").content("{\"a\": 5 }").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Varies`", "one")); } - @Test - public void applicationXmlRequestFields() throws IOException { - xmlRequestFields(MediaType.APPLICATION_XML); + @RenderedSnippetTest + void applicationXmlRequestFields(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + xmlRequestFields(MediaType.APPLICATION_XML, operationBuilder, snippets); } - @Test - public void textXmlRequestFields() throws IOException { - xmlRequestFields(MediaType.TEXT_XML); + @RenderedSnippetTest + void textXmlRequestFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + xmlRequestFields(MediaType.TEXT_XML, operationBuilder, snippets); } - @Test - public void customXmlRequestFields() throws IOException { - xmlRequestFields(MediaType.parseMediaType("application/vnd.com.example+xml")); + @RenderedSnippetTest + void customXmlRequestFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + xmlRequestFields(MediaType.parseMediaType("application/vnd.com.example+xml"), operationBuilder, snippets); } - private void xmlRequestFields(MediaType contentType) throws IOException { + private void xmlRequestFields(MediaType contentType, OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one").type("b"), fieldWithPath("a/c").description("two").type("c"), fieldWithPath("a").description("three").type("a"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("5charlie") .header(HttpHeaders.CONTENT_TYPE, contentType.toString()) .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a/b`", "`b`", "one") - .row("`a/c`", "`c`", "two") - .row("`a`", "`a`", "three")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a/b`", "`b`", "one") + .row("`a/c`", "`c`", "two") + .row("`a`", "`a`", "three")); } - @Test - public void entireSubsectionOfXmlPayloadCanBeDocumented() throws IOException { + @RenderedSnippetTest + void entireSubsectionOfXmlPayloadCanBeDocumented(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(subsectionWithPath("a").description("one").type("a"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("5charlie") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`a`", "one")); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`a`", "one")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { PayloadDocumentation .requestFields(fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two")) .and(fieldWithPath("a").description("three")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a.b`", "`Number`", "one") + .row("`a.c`", "`String`", "two") + .row("`a`", "`Object`", "three")); } - @Test - public void prefixedAdditionalDescriptors() throws IOException { + @RenderedSnippetTest + void prefixedAdditionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { PayloadDocumentation.requestFields(fieldWithPath("a").description("one")) .andWithPrefix("a.", fieldWithPath("b").description("two"), fieldWithPath("c").description("three")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Object`", "one") - .row("`a.b`", "`Number`", "two") - .row("`a.c`", "`String`", "three")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a`", "`Object`", "one") + .row("`a.b`", "`Number`", "two") + .row("`a.c`", "`String`", "three")); } - @Test - public void requestWithFieldsWithEscapedContent() throws IOException { + @RenderedSnippetTest + void requestWithFieldsWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("Foo|Bar").type("one|two").description("three|four"))) - .document(this.operationBuilder.request("http://localhost/").content("{\"Foo|Bar\": 5}").build()); - assertThat(this.generatedSnippets.requestFields()).is(tableWithHeader("Path", "Type", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("`one|two`"), escapeIfNecessary("three|four"))); + .document(operationBuilder.request("http://localhost/").content("{\"Foo|Bar\": 5}").build()); + assertThat(snippets.requestFields()).isTable( + (table) -> table.withHeader("Path", "Type", "Description").row("`Foo|Bar`", "`one|two`", "three|four")); } - @Test - public void mapRequestWithVaryingKeysMatchedUsingWildcard() throws IOException { + @RenderedSnippetTest + void mapRequestWithVaryingKeysMatchedUsingWildcard(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet(Arrays.asList(fieldWithPath("things.*.size").description("one"), fieldWithPath("things.*.type").description("two"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"things\": {\"12abf\": {\"type\":" + "\"Whale\", \"size\": \"HUGE\"}," + "\"gzM33\" : {\"type\": \"Screw\"," + "\"size\": \"SMALL\"}}}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`things.*.size`", "`String`", "one") - .row("`things.*.type`", "`String`", "two")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`things.*.size`", "`String`", "one") + .row("`things.*.type`", "`String`", "two")); } - @Test - public void requestWithArrayContainingFieldThatIsSometimesNull() throws IOException { + @RenderedSnippetTest + void requestWithArrayContainingFieldThatIsSometimesNull(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new RequestFieldsSnippet( Arrays.asList(fieldWithPath("assets[].name").description("one").type(JsonFieldType.STRING).optional())) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"assets\": [" + "{\"name\": \"sample1\"}, " + "{\"name\": null}, " + "{\"name\": \"sample2\"}]}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`assets[].name`", "`String`", "one")); + assertThat(snippets.requestFields()).isTable( + (table) -> table.withHeader("Path", "Type", "Description").row("`assets[].name`", "`String`", "one")); } - @Test - public void optionalFieldBeneathArrayThatIsSometimesAbsent() throws IOException { + @RenderedSnippetTest + void optionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestFieldsSnippet( Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER).optional(), fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") .build()); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a[].b`", "`Number`", "one") - .row("`a[].c`", "`Number`", "two")); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a[].b`", "`Number`", "one") + .row("`a[].c`", "`Number`", "two")); } - @Test - public void typeDeterminationDoesNotSetTypeOnDescriptor() throws IOException { + @RenderedSnippetTest + void typeDeterminationDoesNotSetTypeOnDescriptor(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { FieldDescriptor descriptor = fieldWithPath("a.b").description("one"); new RequestFieldsSnippet(Arrays.asList(descriptor)) - .document(this.operationBuilder.request("http://localhost/").content("{\"a\": {\"b\": 5}}").build()); + .document(operationBuilder.request("http://localhost/").content("{\"a\": {\"b\": 5}}").build()); assertThat(descriptor.getType()).isNull(); - assertThat(this.generatedSnippets.requestFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one")); - } - - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one")); + } + + @SnippetTest + void undocumentedRequestField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) + .document(operationBuilder.request("http://localhost/").content("{\"a\": 5}").build())) + .withMessageStartingWith("The following parts of the payload were not documented:"); + } + + @SnippetTest + void missingRequestField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) + .document(operationBuilder.request("http://localhost/").content("{}").build())) + .withMessage("Fields with the following paths were not found in the payload: [a.b]"); + } + + @SnippetTest + void missingOptionalRequestFieldWithNoTypeProvided(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypeRequiredException.class).isThrownBy( + () -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").optional())) + .document(operationBuilder.request("http://localhost/").content("{ }").build())); + } + + @SnippetTest + void undocumentedRequestFieldAndMissingRequestField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) + .document(operationBuilder.request("http://localhost/").content("{ \"a\": { \"c\": 5 }}").build())) + .withMessageStartingWith("The following parts of the payload were not documented:") + .withMessageEndingWith("Fields with the following paths were not found in the payload: [a.b]"); + } + + @SnippetTest + void attemptToDocumentFieldsWithNoRequestBody(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) + .document(operationBuilder.request("http://localhost/").build())) + .withMessage("Cannot document request fields as the request body is empty"); + } + + @SnippetTest + void fieldWithExplicitTypeThatDoesNotMatchThePayload(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypesDoNotMatchException.class) + .isThrownBy(() -> new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.OBJECT))) + .document(operationBuilder.request("http://localhost/").content("{ \"a\": 5 }").build())) + .withMessage("The documented type of the field 'a' is Object but the actual type is Number"); + } + + @SnippetTest + void fieldWithExplicitSpecificTypeThatActuallyVaries(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypesDoNotMatchException.class).isThrownBy(() -> new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("[].a").description("one").type(JsonFieldType.OBJECT))) + .document(operationBuilder.request("http://localhost/").content("[{ \"a\": 5 },{ \"a\": \"b\" }]").build())) + .withMessage("The documented type of the field '[].a' is Object but the actual type is Varies"); + } + + @SnippetTest + void undocumentedXmlRequestField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) + .document(operationBuilder.request("http://localhost/") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessageStartingWith("The following parts of the payload were not documented:"); + } + + @SnippetTest + void xmlDescendentsAreNotDocumentedByFieldDescriptor(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").type("a").description("one"))) + .document(operationBuilder.request("http://localhost/") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessageStartingWith("The following parts of the payload were not documented:"); + } + + @SnippetTest + void xmlRequestFieldWithNoType(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypeRequiredException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) + .document(operationBuilder.request("http://localhost/") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())); + } + + @SnippetTest + void missingXmlRequestField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("a/b").description("one"), fieldWithPath("a").description("one"))) + .document(operationBuilder.request("http://localhost/") + .content("") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessage("Fields with the following paths were not found in the payload: [a/b]"); + } + + @SnippetTest + void undocumentedXmlRequestFieldAndMissingXmlRequestField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) + .document(operationBuilder.request("http://localhost/") + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessageStartingWith("The following parts of the payload were not documented:") + .withMessageEndingWith("Fields with the following paths were not found in the payload: [a/b]"); + } + + @SnippetTest + void unsupportedContent(OperationBuilder operationBuilder) { + assertThatExceptionOfType(PayloadHandlingException.class) + .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) + .document(operationBuilder.request("http://localhost/") + .content("Some plain text") + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) + .build())) + .withMessage("Cannot handle text/plain content as it could not be parsed as JSON or XML"); + } + + @SnippetTest + void nonOptionalFieldBeneathArrayThatIsSometimesNull(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), + fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) + .document(operationBuilder.request("http://localhost/") + .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"b\": null, \"c\": 2}," + " {\"b\": 1,\"c\": 2}]}") + .build())) + .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); + } + + @SnippetTest + void nonOptionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), + fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) + .document(operationBuilder.request("http://localhost/") + .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") + .build())) + .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetFailureTests.java deleted file mode 100644 index 52f1cead..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetFailureTests.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; - -/** - * Tests for failures when rendering {@link RequestPartFieldsSnippet} due to missing or - * undocumented fields. - * - * @author Mathieu Pousse - * @author Andy Wilkinson - */ -public class RequestPartFieldsSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedRequestPartField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartFieldsSnippet("part", Collections.emptyList()).document( - this.operationBuilder.request("http://localhost/").part("part", "{\"a\": 5}".getBytes()).build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @Test - public void missingRequestPartField() { - assertThatExceptionOfType(SnippetException.class).isThrownBy(() -> new RequestPartFieldsSnippet("part", - Arrays.asList(fieldWithPath("b").description("one"))) - .document(this.operationBuilder.request("http://localhost/").part("part", "{\"a\": 5}".getBytes()).build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @Test - public void missingRequestPart() { - assertThatExceptionOfType(SnippetException.class).isThrownBy( - () -> new RequestPartFieldsSnippet("another", Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(this.operationBuilder.request("http://localhost/") - .part("part", "{\"a\": {\"b\": 5}}".getBytes()) - .build())) - .withMessage("A request part named 'another' was not found in the request"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java index 7967bc7d..f4924d75 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,13 +20,15 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -36,86 +38,110 @@ * @author Mathieu Pousse * @author Andy Wilkinson */ -public class RequestPartFieldsSnippetTests extends AbstractSnippetTests { - - public RequestPartFieldsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +public class RequestPartFieldsSnippetTests { - @Test - public void mapRequestPartFields() throws IOException { + @RenderedSnippetTest + void mapRequestPartFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestPartFieldsSnippet("one", Arrays.asList(fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two"), fieldWithPath("a").description("three"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) .build()); - assertThat(this.generatedSnippets.requestPartFields("one")) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); + assertThat(snippets.requestPartFields("one")).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a.b`", "`Number`", "one") + .row("`a.c`", "`String`", "two") + .row("`a`", "`Object`", "three")); } - @Test - public void mapRequestPartSubsectionFields() throws IOException { + @RenderedSnippetTest + void mapRequestPartSubsectionFields(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestPartFieldsSnippet("one", beneathPath("a"), Arrays.asList(fieldWithPath("b").description("one"), fieldWithPath("c").description("two"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) .build()); - assertThat(this.generatedSnippets.snippet("request-part-one-fields-beneath-a")) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "one") + assertThat(snippets.requestPartFields("one", "beneath-a")) + .isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`b`", "`Number`", "one") .row("`c`", "`String`", "two")); } - @Test - public void multipleRequestParts() throws IOException { - Operation operation = this.operationBuilder.request("http://localhost/") + @RenderedSnippetTest + void multipleRequestParts(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + Operation operation = operationBuilder.request("http://localhost/") .part("one", "{}".getBytes()) .and() .part("two", "{}".getBytes()) .build(); new RequestPartFieldsSnippet("one", Collections.emptyList()).document(operation); new RequestPartFieldsSnippet("two", Collections.emptyList()).document(operation); - assertThat(this.generatedSnippets.requestPartFields("one")).isNotNull(); - assertThat(this.generatedSnippets.requestPartFields("two")).isNotNull(); + assertThat(snippets.requestPartFields("one")).isNotNull(); + assertThat(snippets.requestPartFields("two")).isNotNull(); } - @Test - public void allUndocumentedRequestPartFieldsCanBeIgnored() throws IOException { - new RequestPartFieldsSnippet("one", Arrays.asList(fieldWithPath("b").description("Field b")), true) - .document(this.operationBuilder.request("http://localhost/") - .part("one", "{\"a\": 5, \"b\": 4}".getBytes()) - .build()); - assertThat(this.generatedSnippets.requestPartFields("one")) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); + @RenderedSnippetTest + void allUndocumentedRequestPartFieldsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new RequestPartFieldsSnippet("one", Arrays.asList(fieldWithPath("b").description("Field b")), true).document( + operationBuilder.request("http://localhost/").part("one", "{\"a\": 5, \"b\": 4}".getBytes()).build()); + assertThat(snippets.requestPartFields("one")) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { PayloadDocumentation .requestPartFields("one", fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two")) .and(fieldWithPath("a").description("three")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) .build()); - assertThat(this.generatedSnippets.requestPartFields("one")) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); + assertThat(snippets.requestPartFields("one")).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a.b`", "`Number`", "one") + .row("`a.c`", "`String`", "two") + .row("`a`", "`Object`", "three")); } - @Test - public void prefixedAdditionalDescriptors() throws IOException { + @RenderedSnippetTest + void prefixedAdditionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { PayloadDocumentation.requestPartFields("one", fieldWithPath("a").description("one")) .andWithPrefix("a.", fieldWithPath("b").description("two"), fieldWithPath("c").description("three")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) .build()); - assertThat(this.generatedSnippets.requestPartFields("one")) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Object`", "one") - .row("`a.b`", "`Number`", "two") - .row("`a.c`", "`String`", "three")); + assertThat(snippets.requestPartFields("one")).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a`", "`Object`", "one") + .row("`a.b`", "`Number`", "two") + .row("`a.c`", "`String`", "three")); + } + + @SnippetTest + void undocumentedRequestPartField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestPartFieldsSnippet("part", Collections.emptyList()) + .document(operationBuilder.request("http://localhost/").part("part", "{\"a\": 5}".getBytes()).build())) + .withMessageStartingWith("The following parts of the payload were not documented:"); + } + + @SnippetTest + void missingRequestPartField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestPartFieldsSnippet("part", Arrays.asList(fieldWithPath("b").description("one"))) + .document(operationBuilder.request("http://localhost/").part("part", "{\"a\": 5}".getBytes()).build())) + .withMessageStartingWith("The following parts of the payload were not documented:"); + } + + @SnippetTest + void missingRequestPart(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class).isThrownBy( + () -> new RequestPartFieldsSnippet("another", Arrays.asList(fieldWithPath("a.b").description("one"))) + .document(operationBuilder.request("http://localhost/") + .part("part", "{\"a\": {\"b\": 5}}".getBytes()) + .build())) + .withMessage("A request part named 'another' was not found in the request"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java index ee4f34b8..74c65995 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,14 @@ import java.io.IOException; -import org.junit.Test; - import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody; import static org.springframework.restdocs.snippet.Attributes.attributes; @@ -41,77 +36,72 @@ * * @author Andy Wilkinson */ -public class ResponseBodySnippetTests extends AbstractSnippetTests { +class ResponseBodySnippetTests { - public ResponseBodySnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); + @RenderedSnippetTest + void responseWithBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new ResponseBodySnippet().document(operationBuilder.response().content("some content").build()); + assertThat(snippets.responseBody()) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("some content")); } - @Test - public void responseWithBody() throws IOException { - new ResponseBodySnippet().document(this.operationBuilder.response().content("some content").build()); - assertThat(this.generatedSnippets.snippet("response-body")) - .is(codeBlock(null, "nowrap").withContent("some content")); + @RenderedSnippetTest + void responseWithNoBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new ResponseBodySnippet().document(operationBuilder.response().build()); + assertThat(snippets.responseBody()).isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("")); } - @Test - public void responseWithNoBody() throws IOException { - new ResponseBodySnippet().document(this.operationBuilder.response().build()); - assertThat(this.generatedSnippets.snippet("response-body")).is(codeBlock(null, "nowrap").withContent("")); + @RenderedSnippetTest + void responseWithJsonMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new ResponseBodySnippet().document( + operationBuilder.response().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build()); + assertThat(snippets.responseBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); } - @Test - public void responseWithJsonMediaType() throws IOException { - new ResponseBodySnippet().document(this.operationBuilder.response() - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .build()); - assertThat(this.generatedSnippets.snippet("response-body")).is(codeBlock("json", "nowrap").withContent("")); - } - - @Test - public void responseWithJsonSubtypeMediaType() throws IOException { - new ResponseBodySnippet().document(this.operationBuilder.response() + @RenderedSnippetTest + void responseWithJsonSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new ResponseBodySnippet().document(operationBuilder.response() .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("response-body")).is(codeBlock("json", "nowrap").withContent("")); + assertThat(snippets.responseBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); } - @Test - public void responseWithXmlMediaType() throws IOException { - new ResponseBodySnippet().document(this.operationBuilder.response() - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(this.generatedSnippets.snippet("response-body")).is(codeBlock("xml", "nowrap").withContent("")); + @RenderedSnippetTest + void responseWithXmlMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new ResponseBodySnippet().document( + operationBuilder.response().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE).build()); + assertThat(snippets.responseBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); } - @Test - public void responseWithXmlSubtypeMediaType() throws IOException { - new ResponseBodySnippet().document(this.operationBuilder.response() + @RenderedSnippetTest + void responseWithXmlSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new ResponseBodySnippet().document(operationBuilder.response() .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML_VALUE) .build()); - assertThat(this.generatedSnippets.snippet("response-body")).is(codeBlock("xml", "nowrap").withContent("")); + assertThat(snippets.responseBody()) + .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); } - @Test - public void subsectionOfResponseBody() throws IOException { + @RenderedSnippetTest + void subsectionOfResponseBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { responseBody(beneathPath("a.b")) - .document(this.operationBuilder.response().content("{\"a\":{\"b\":{\"c\":5}}}").build()); - assertThat(this.generatedSnippets.snippet("response-body-beneath-a.b")) - .is(codeBlock(null, "nowrap").withContent("{\"c\":5}")); + .document(operationBuilder.response().content("{\"a\":{\"b\":{\"c\":5}}}").build()); + assertThat(snippets.responseBody("beneath-a.b")) + .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("{\"c\":5}")); } - @Test - public void customSnippetAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-body")) - .willReturn(snippetResource("response-body-with-language")); - new ResponseBodySnippet(attributes(key("language").value("json"))).document( - this.operationBuilder.attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() - .content("{\"a\":\"alpha\"}") - .build()); - assertThat(this.generatedSnippets.snippet("response-body")) - .is(codeBlock("json", "nowrap").withContent("{\"a\":\"alpha\"}")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-body", template = "response-body-with-language") + void customSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new ResponseBodySnippet(attributes(key("language").value("json"))) + .document(operationBuilder.response().content("{\"a\":\"alpha\"}").build()); + assertThat(snippets.responseBody()).isCodeBlock( + (codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("{\"a\":\"alpha\"}")); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java deleted file mode 100644 index 3e41d9fe..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetFailureTests.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; - -/** - * Tests for failures when rendering {@link ResponseFieldsSnippet} due to missing or - * undocumented fields. - * - * @author Andy Wilkinson - */ -public class ResponseFieldsSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void attemptToDocumentFieldsWithNoResponseBody() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(this.operationBuilder.build())) - .withMessage("Cannot document response fields as the response body is empty"); - } - - @Test - public void fieldWithExplicitTypeThatDoesNotMatchThePayload() { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.OBJECT))) - .document(this.operationBuilder.response().content("{ \"a\": 5 }}").build())) - .withMessage("The documented type of the field 'a' is Object but the actual type is Number"); - } - - @Test - public void fieldWithExplicitSpecificTypeThatActuallyVaries() { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("[].a").description("one").type(JsonFieldType.OBJECT))) - .document(this.operationBuilder.response().content("[{ \"a\": 5 },{ \"a\": \"b\" }]").build())) - .withMessage("The documented type of the field '[].a' is Object but the actual type is Varies"); - } - - @Test - public void undocumentedXmlResponseField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Collections.emptyList()) - .document(this.operationBuilder.response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @Test - public void missingXmlAttribute() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), - fieldWithPath("a/@id").description("two").type("c"))) - .document(this.operationBuilder.response() - .content("foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage("Fields with the following paths were not found in the payload: [a/@id]"); - } - - @Test - public void documentedXmlAttributesAreRemoved() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy( - () -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one").type("a"))) - .document(this.operationBuilder.response() - .content("bar") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage(String.format("The following parts of the payload were not documented:%nbar%n")); - } - - @Test - public void xmlResponseFieldWithNoType() { - assertThatExceptionOfType(FieldTypeRequiredException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(this.operationBuilder.response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())); - } - - @Test - public void missingXmlResponseField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a/b").description("one"), fieldWithPath("a").description("one"))) - .document(this.operationBuilder.response() - .content("") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage("Fields with the following paths were not found in the payload: [a/b]"); - } - - @Test - public void undocumentedXmlResponseFieldAndMissingXmlResponseField() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) - .document(this.operationBuilder.response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:") - .withMessageEndingWith("Fields with the following paths were not found in the payload: [a/b]"); - } - - @Test - public void unsupportedContent() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Collections.emptyList()) - .document(this.operationBuilder.response() - .content("Some plain text") - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .build())) - .withMessage("Cannot handle text/plain content as it could not be parsed as JSON or XML"); - } - - @Test - public void nonOptionalFieldBeneathArrayThatIsSometimesNull() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.response() - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"b\": null, \"c\": 2}," + " {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - - @Test - public void nonOptionalFieldBeneathArrayThatIsSometimesAbsent() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.response() - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java index 8372ae5e..e238851b 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -18,21 +18,19 @@ import java.io.IOException; import java.util.Arrays; - -import org.junit.Test; +import java.util.Collections; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; @@ -45,355 +43,486 @@ * @author Andy Wilkinson * @author Sungjun Lee */ -public class ResponseFieldsSnippetTests extends AbstractSnippetTests { - - public ResponseFieldsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +public class ResponseFieldsSnippetTests { - @Test - public void mapResponseWithFields() throws IOException { + @RenderedSnippetTest + void mapResponseWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("id").description("one"), fieldWithPath("date").description("two"), fieldWithPath("assets").description("three"), fieldWithPath("assets[]").description("four"), fieldWithPath("assets[].id").description("five"), fieldWithPath("assets[].name").description("six"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":" + " [{\"id\":356,\"name\": \"sample\"}]}") .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`id`", "`Number`", "one") - .row("`date`", "`String`", "two") - .row("`assets`", "`Array`", "three") - .row("`assets[]`", "`Array`", "four") - .row("`assets[].id`", "`Number`", "five") - .row("`assets[].name`", "`String`", "six")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`id`", "`Number`", "one") + .row("`date`", "`String`", "two") + .row("`assets`", "`Array`", "three") + .row("`assets[]`", "`Array`", "four") + .row("`assets[].id`", "`Number`", "five") + .row("`assets[].name`", "`String`", "six")); } - @Test - public void mapResponseWithNullField() throws IOException { + @RenderedSnippetTest + void mapResponseWithNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(this.operationBuilder.response().content("{\"a\": {\"b\": null}}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`Null`", "one")); + .document(operationBuilder.response().content("{\"a\": {\"b\": null}}").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`Null`", "one")); } - @Test - public void subsectionOfMapResponse() throws IOException { + @RenderedSnippetTest + void subsectionOfMapResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { responseFields(beneathPath("a"), fieldWithPath("b").description("one"), fieldWithPath("c").description("two")) - .document(this.operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); - assertThat(this.generatedSnippets.snippet("response-fields-beneath-a")) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "one") + .document(operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); + assertThat(snippets.responseFields("beneath-a")) + .isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`b`", "`Number`", "one") .row("`c`", "`String`", "two")); } - @Test - public void subsectionOfMapResponseBeneathAnArray() throws IOException { + @RenderedSnippetTest + void subsectionOfMapResponseBeneathAnArray(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { responseFields(beneathPath("a.b.[]"), fieldWithPath("c").description("one"), fieldWithPath("d.[].e").description("two")) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("{\"a\": {\"b\": [{\"c\": 1, \"d\": [{\"e\": 5}]}, {\"c\": 3, \"d\": [{\"e\": 4}]}]}}") .build()); - assertThat(this.generatedSnippets.snippet("response-fields-beneath-a.b.[]")) - .is(tableWithHeader("Path", "Type", "Description").row("`c`", "`Number`", "one") + assertThat(snippets.responseFields("beneath-a.b.[]")) + .isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`c`", "`Number`", "one") .row("`d.[].e`", "`Number`", "two")); } - @Test - public void subsectionOfMapResponseWithCommonsPrefix() throws IOException { + @RenderedSnippetTest + void subsectionOfMapResponseWithCommonsPrefix(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { responseFields(beneathPath("a")).andWithPrefix("b.", fieldWithPath("c").description("two")) - .document(this.operationBuilder.response().content("{\"a\": {\"b\": {\"c\": \"charlie\"}}}").build()); - assertThat(this.generatedSnippets.snippet("response-fields-beneath-a")) - .is(tableWithHeader("Path", "Type", "Description").row("`b.c`", "`String`", "two")); + .document(operationBuilder.response().content("{\"a\": {\"b\": {\"c\": \"charlie\"}}}").build()); + assertThat(snippets.responseFields("beneath-a")) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b.c`", "`String`", "two")); } - @Test - public void arrayResponseWithFields() throws IOException { + @RenderedSnippetTest + void arrayResponseWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"), fieldWithPath("[]a.c").description("two"), fieldWithPath("[]a").description("three"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("[{\"a\": {\"b\": 5, \"c\":\"charlie\"}}," + "{\"a\": {\"b\": 4, \"c\":\"chalk\"}}]") .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`[]a.b`", "`Number`", "one") - .row("`[]a.c`", "`String`", "two") - .row("`[]a`", "`Object`", "three")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`[]a.b`", "`Number`", "one") + .row("`[]a.c`", "`String`", "two") + .row("`[]a`", "`Object`", "three")); } - @Test - public void arrayResponseWithAlwaysNullField() throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"))) - .document(this.operationBuilder.response() - .content("[{\"a\": {\"b\": null}}," + "{\"a\": {\"b\": null}}]") - .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`[]a.b`", "`Null`", "one")); + @RenderedSnippetTest + void arrayResponseWithAlwaysNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"))).document( + operationBuilder.response().content("[{\"a\": {\"b\": null}}," + "{\"a\": {\"b\": null}}]").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`[]a.b`", "`Null`", "one")); } - @Test - public void arrayResponse() throws IOException { + @RenderedSnippetTest + void arrayResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]").description("one"))) - .document(this.operationBuilder.response().content("[\"a\", \"b\", \"c\"]").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`[]`", "`Array`", "one")); + .document(operationBuilder.response().content("[\"a\", \"b\", \"c\"]").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`[]`", "`Array`", "one")); } - @Test - public void ignoredResponseField() throws IOException { + @RenderedSnippetTest + void ignoredResponseField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet( Arrays.asList(fieldWithPath("a").ignored(), fieldWithPath("b").description("Field b"))) - .document(this.operationBuilder.response().content("{\"a\": 5, \"b\": 4}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); + .document(operationBuilder.response().content("{\"a\": 5, \"b\": 4}").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); } - @Test - public void allUndocumentedFieldsCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedFieldsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) - .document(this.operationBuilder.response().content("{\"a\": 5, \"b\": 4}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); + .document(operationBuilder.response().content("{\"a\": 5, \"b\": 4}").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); } - @Test - public void allUndocumentedFieldsContinueToBeIgnoredAfterAddingDescriptors() throws IOException { + @RenderedSnippetTest + void allUndocumentedFieldsContinueToBeIgnoredAfterAddingDescriptors(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) .andWithPrefix("c.", fieldWithPath("d").description("Field d")) - .document(this.operationBuilder.response().content("{\"a\":5,\"b\":4,\"c\":{\"d\": 3}}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b") - .row("`c.d`", "`Number`", "Field d")); + .document(operationBuilder.response().content("{\"a\":5,\"b\":4,\"c\":{\"d\": 3}}").build()); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`b`", "`Number`", "Field b") + .row("`c.d`", "`Number`", "Field d")); } - @Test - public void responseFieldsWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-fields")) - .willReturn(snippetResource("response-fields-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-fields", template = "response-fields-with-title") + void responseFieldsWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), attributes(key("title").value("Custom title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() - .content("{\"a\": \"foo\"}") - .build()); - assertThat(this.generatedSnippets.responseFields()).contains("Custom title"); + .document(operationBuilder.response().content("{\"a\": \"foo\"}").build()); + assertThat(snippets.responseFields()).contains("Custom title"); } - @Test - public void missingOptionalResponseField() throws IOException { + @RenderedSnippetTest + void missingOptionalResponseField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet( Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(this.operationBuilder.response().content("{}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); + .document(operationBuilder.response().content("{}").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); } - @Test - public void missingIgnoredOptionalResponseFieldDoesNotRequireAType() throws IOException { + @RenderedSnippetTest + void missingIgnoredOptionalResponseFieldDoesNotRequireAType(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").ignored().optional())) - .document(this.operationBuilder.response().content("{}").build()); - assertThat(this.generatedSnippets.responseFields()).is(tableWithHeader("Path", "Type", "Description")); + .document(operationBuilder.response().content("{}").build()); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description")); } - @Test - public void presentOptionalResponseField() throws IOException { + @RenderedSnippetTest + void presentOptionalResponseField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet( Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(this.operationBuilder.response().content("{\"a\": { \"b\": \"bravo\"}}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); + .document(operationBuilder.response().content("{\"a\": { \"b\": \"bravo\"}}").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); } - @Test - public void responseFieldsWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("response-fields")) - .willReturn(snippetResource("response-fields-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "response-fields", template = "response-fields-with-extra-column") + void responseFieldsWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet( Arrays.asList(fieldWithPath("a.b").description("one").attributes(key("foo").value("alpha")), fieldWithPath("a.c").description("two").attributes(key("foo").value("bravo")), fieldWithPath("a").description("three").attributes(key("foo").value("charlie")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .response() - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description", "Foo").row("a.b", "Number", "one", "alpha") - .row("a.c", "String", "two", "bravo") - .row("a", "Object", "three", "charlie")); + .document(operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description", "Foo") + .row("a.b", "Number", "one", "alpha") + .row("a.c", "String", "two", "bravo") + .row("a", "Object", "three", "charlie")); } - @Test - public void fieldWithExplicitExactlyMatchingType() throws IOException { + @RenderedSnippetTest + void fieldWithExplicitExactlyMatchingType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.response().content("{\"a\": 5 }").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Number`", "one")); + .document(operationBuilder.response().content("{\"a\": 5 }").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Number`", "one")); } - @Test - public void fieldWithExplicitVariesType() throws IOException { + @RenderedSnippetTest + void fieldWithExplicitVariesType(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.VARIES))) - .document(this.operationBuilder.response().content("{\"a\": 5 }").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Varies`", "one")); + .document(operationBuilder.response().content("{\"a\": 5 }").build()); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Varies`", "one")); } - @Test - public void applicationXmlResponseFields() throws IOException { - xmlResponseFields(MediaType.APPLICATION_XML); + @RenderedSnippetTest + void applicationXmlResponseFields(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + xmlResponseFields(MediaType.APPLICATION_XML, operationBuilder, snippets); } - @Test - public void textXmlResponseFields() throws IOException { - xmlResponseFields(MediaType.TEXT_XML); + @RenderedSnippetTest + void textXmlResponseFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + xmlResponseFields(MediaType.TEXT_XML, operationBuilder, snippets); } - @Test - public void customXmlResponseFields() throws IOException { - xmlResponseFields(MediaType.parseMediaType("application/vnd.com.example+xml")); + @RenderedSnippetTest + void customXmlResponseFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + xmlResponseFields(MediaType.parseMediaType("application/vnd.com.example+xml"), operationBuilder, snippets); } - private void xmlResponseFields(MediaType contentType) throws IOException { + private void xmlResponseFields(MediaType contentType, OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one").type("b"), fieldWithPath("a/c").description("two").type("c"), fieldWithPath("a").description("three").type("a"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("5charlie") .header(HttpHeaders.CONTENT_TYPE, contentType.toString()) .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a/b`", "`b`", "one") - .row("`a/c`", "`c`", "two") - .row("`a`", "`a`", "three")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a/b`", "`b`", "one") + .row("`a/c`", "`c`", "two") + .row("`a`", "`a`", "three")); } - @Test - public void xmlAttribute() throws IOException { + @RenderedSnippetTest + void xmlAttribute(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), fieldWithPath("a/@id").description("two").type("c"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`b`", "one").row("`a/@id`", "`c`", "two")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a`", "`b`", "one") + .row("`a/@id`", "`c`", "two")); } - @Test - public void missingOptionalXmlAttribute() throws IOException { + @RenderedSnippetTest + void missingOptionalXmlAttribute(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), fieldWithPath("a/@id").description("two").type("c").optional())) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("foo") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`b`", "one").row("`a/@id`", "`c`", "two")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a`", "`b`", "one") + .row("`a/@id`", "`c`", "two")); } - @Test - public void undocumentedAttributeDoesNotCauseFailure() throws IOException { + @RenderedSnippetTest + void undocumentedAttributeDoesNotCauseFailure(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("a"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("bar") .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`a`", "one")); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`a`", "one")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { PayloadDocumentation .responseFields(fieldWithPath("id").description("one"), fieldWithPath("date").description("two"), fieldWithPath("assets").description("three")) .and(fieldWithPath("assets[]").description("four"), fieldWithPath("assets[].id").description("five"), fieldWithPath("assets[].name").description("six")) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":" + " [{\"id\":356,\"name\": \"sample\"}]}") .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`id`", "`Number`", "one") - .row("`date`", "`String`", "two") - .row("`assets`", "`Array`", "three") - .row("`assets[]`", "`Array`", "four") - .row("`assets[].id`", "`Number`", "five") - .row("`assets[].name`", "`String`", "six")); - } - - @Test - public void prefixedAdditionalDescriptors() throws IOException { + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`id`", "`Number`", "one") + .row("`date`", "`String`", "two") + .row("`assets`", "`Array`", "three") + .row("`assets[]`", "`Array`", "four") + .row("`assets[].id`", "`Number`", "five") + .row("`assets[].name`", "`String`", "six")); + } + + @RenderedSnippetTest + void prefixedAdditionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { PayloadDocumentation.responseFields(fieldWithPath("a").description("one")) .andWithPrefix("a.", fieldWithPath("b").description("two"), fieldWithPath("c").description("three")) - .document(this.operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a`", "`Object`", "one") - .row("`a.b`", "`Number`", "two") - .row("`a.c`", "`String`", "three")); + .document(operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a`", "`Object`", "one") + .row("`a.b`", "`Number`", "two") + .row("`a.c`", "`String`", "three")); } - @Test - public void responseWithFieldsWithEscapedContent() throws IOException { + @RenderedSnippetTest + void responseWithFieldsWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("Foo|Bar").type("one|two").description("three|four"))) - .document(this.operationBuilder.response().content("{\"Foo|Bar\": 5}").build()); - assertThat(this.generatedSnippets.responseFields()).is(tableWithHeader("Path", "Type", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("`one|two`"), escapeIfNecessary("three|four"))); + .document(operationBuilder.response().content("{\"Foo|Bar\": 5}").build()); + assertThat(snippets.responseFields()).isTable( + (table) -> table.withHeader("Path", "Type", "Description").row("`Foo|Bar`", "`one|two`", "three|four")); } - @Test - public void mapResponseWithVaryingKeysMatchedUsingWildcard() throws IOException { + @RenderedSnippetTest + void mapResponseWithVaryingKeysMatchedUsingWildcard(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("things.*.size").description("one"), fieldWithPath("things.*.type").description("two"))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("{\"things\": {\"12abf\": {\"type\":" + "\"Whale\", \"size\": \"HUGE\"}," + "\"gzM33\" : {\"type\": \"Screw\"," + "\"size\": \"SMALL\"}}}") .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`things.*.size`", "`String`", "one") - .row("`things.*.type`", "`String`", "two")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`things.*.size`", "`String`", "one") + .row("`things.*.type`", "`String`", "two")); } - @Test - public void responseWithArrayContainingFieldThatIsSometimesNull() throws IOException { + @RenderedSnippetTest + void responseWithArrayContainingFieldThatIsSometimesNull(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { new ResponseFieldsSnippet( Arrays.asList(fieldWithPath("assets[].name").description("one").type(JsonFieldType.STRING).optional())) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("{\"assets\": [" + "{\"name\": \"sample1\"}, " + "{\"name\": null}, " + "{\"name\": \"sample2\"}]}") .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`assets[].name`", "`String`", "one")); + assertThat(snippets.responseFields()).isTable( + (table) -> table.withHeader("Path", "Type", "Description").row("`assets[].name`", "`String`", "one")); } - @Test - public void optionalFieldBeneathArrayThatIsSometimesAbsent() throws IOException { + @RenderedSnippetTest + void optionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new ResponseFieldsSnippet( Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER).optional(), fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(this.operationBuilder.response() + .document(operationBuilder.response() .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") .build()); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`a[].b`", "`Number`", "one") - .row("`a[].c`", "`Number`", "two")); + assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") + .row("`a[].b`", "`Number`", "one") + .row("`a[].c`", "`Number`", "two")); } - @Test - public void typeDeterminationDoesNotSetTypeOnDescriptor() throws IOException { + @RenderedSnippetTest + void typeDeterminationDoesNotSetTypeOnDescriptor(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { FieldDescriptor descriptor = fieldWithPath("id").description("one"); new ResponseFieldsSnippet(Arrays.asList(descriptor)) - .document(this.operationBuilder.response().content("{\"id\": 67}").build()); + .document(operationBuilder.response().content("{\"id\": 67}").build()); assertThat(descriptor.getType()).isNull(); - assertThat(this.generatedSnippets.responseFields()) - .is(tableWithHeader("Path", "Type", "Description").row("`id`", "`Number`", "one")); - } - - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`id`", "`Number`", "one")); + } + + @SnippetTest + void attemptToDocumentFieldsWithNoResponseBody(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) + .document(operationBuilder.build())) + .withMessage("Cannot document response fields as the response body is empty"); + } + + @SnippetTest + void fieldWithExplicitTypeThatDoesNotMatchThePayload(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypesDoNotMatchException.class) + .isThrownBy(() -> new ResponseFieldsSnippet( + Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.OBJECT))) + .document(operationBuilder.response().content("{ \"a\": 5 }}").build())) + .withMessage("The documented type of the field 'a' is Object but the actual type is Number"); + } + + @SnippetTest + void fieldWithExplicitSpecificTypeThatActuallyVaries(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypesDoNotMatchException.class) + .isThrownBy(() -> new ResponseFieldsSnippet( + Arrays.asList(fieldWithPath("[].a").description("one").type(JsonFieldType.OBJECT))) + .document(operationBuilder.response().content("[{ \"a\": 5 },{ \"a\": \"b\" }]").build())) + .withMessage("The documented type of the field '[].a' is Object but the actual type is Varies"); + } + + @SnippetTest + void undocumentedXmlResponseField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet(Collections.emptyList()) + .document(operationBuilder.response() + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessageStartingWith("The following parts of the payload were not documented:"); + } + + @SnippetTest + void missingXmlAttribute(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), + fieldWithPath("a/@id").description("two").type("c"))) + .document(operationBuilder.response() + .content("foo") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessage("Fields with the following paths were not found in the payload: [a/@id]"); + } + + @SnippetTest + void documentedXmlAttributesAreRemoved(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy( + () -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one").type("a"))) + .document(operationBuilder.response() + .content("bar") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessage(String.format("The following parts of the payload were not documented:%nbar%n")); + } + + @SnippetTest + void xmlResponseFieldWithNoType(OperationBuilder operationBuilder) { + assertThatExceptionOfType(FieldTypeRequiredException.class) + .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) + .document(operationBuilder.response() + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())); + } + + @SnippetTest + void missingXmlResponseField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet( + Arrays.asList(fieldWithPath("a/b").description("one"), fieldWithPath("a").description("one"))) + .document(operationBuilder.response() + .content("") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessage("Fields with the following paths were not found in the payload: [a/b]"); + } + + @SnippetTest + void undocumentedXmlResponseFieldAndMissingXmlResponseField(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) + .document(operationBuilder.response() + .content("5") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .build())) + .withMessageStartingWith("The following parts of the payload were not documented:") + .withMessageEndingWith("Fields with the following paths were not found in the payload: [a/b]"); + } + + @SnippetTest + void unsupportedContent(OperationBuilder operationBuilder) { + assertThatExceptionOfType(PayloadHandlingException.class) + .isThrownBy(() -> new ResponseFieldsSnippet(Collections.emptyList()) + .document(operationBuilder.response() + .content("Some plain text") + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) + .build())) + .withMessage("Cannot handle text/plain content as it could not be parsed as JSON or XML"); + } + + @SnippetTest + void nonOptionalFieldBeneathArrayThatIsSometimesNull(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet( + Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), + fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) + .document(operationBuilder.response() + .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"b\": null, \"c\": 2}," + " {\"b\": 1,\"c\": 2}]}") + .build())) + .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); + } + + @SnippetTest + void nonOptionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new ResponseFieldsSnippet( + Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), + fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) + .document(operationBuilder.response() + .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") + .build())) + .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java index fb601c44..6b98dac4 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetFailureTests.java deleted file mode 100644 index 0b200e52..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetFailureTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; - -/** - * Tests for failures when rendering {@link FormParametersSnippet} due to missing or - * undocumented form parameters. - * - * @author Andy Wilkinson - */ -public class FormParametersSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedParameter() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new FormParametersSnippet(Collections.emptyList()) - .document(this.operationBuilder.request("http://localhost/").content("a=alpha").build())) - .withMessage("Form parameters with the following names were not documented: [a]"); - } - - @Test - public void missingParameter() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").build())) - .withMessage("Form parameters with the following names were not found in the request: [a]"); - } - - @Test - public void undocumentedAndMissingParameters() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").content("b=bravo").build())) - .withMessage("Form parameters with the following names were not documented: [b]. Form parameters" - + " with the following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java index 75840975..0cf38369 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,17 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -40,147 +38,151 @@ * * @author Andy Wilkinson */ -public class FormParametersSnippetTests extends AbstractSnippetTests { - - public FormParametersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class FormParametersSnippetTests { - @Test - public void formParameters() throws IOException { + @RenderedSnippetTest + void formParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new FormParametersSnippet( Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void formParameterWithNoValue() throws IOException { + @RenderedSnippetTest + void formParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").content("a=").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one")); + .document(operationBuilder.request("http://localhost/").content("a=").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); } - @Test - public void ignoredFormParameter() throws IOException { + @RenderedSnippetTest + void ignoredFormParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new FormParametersSnippet( Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); } - @Test - public void allUndocumentedFormParametersCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedFormParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new FormParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true) - .document(this.operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); } - @Test - public void missingOptionalFormParameter() throws IOException { + @RenderedSnippetTest + void missingOptionalFormParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), parameterWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/").content("b=bravo").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").content("b=bravo").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void presentOptionalFormParameter() throws IOException { + @RenderedSnippetTest + void presentOptionalFormParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())) - .document(this.operationBuilder.request("http://localhost/").content("a=alpha").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one")); + .document(operationBuilder.request("http://localhost/").content("a=alpha").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); } - @Test - public void formParametersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("form-parameters")) - .willReturn(snippetResource("form-parameters-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "form-parameters", template = "form-parameters-with-title") + void formParametersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new FormParametersSnippet( Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), parameterWithName("b").description("two").attributes(key("foo").value("bravo"))), attributes(key("title").value("The title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .content("a=alpha&b=bravo") - .build()); - assertThat(this.generatedSnippets.formParameters()).contains("The title"); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()).contains("The title"); } - @Test - public void formParametersWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("form-parameters")) - .willReturn(snippetResource("form-parameters-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "form-parameters", template = "form-parameters-with-extra-column") + void formParametersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new FormParametersSnippet( Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), parameterWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .content("a=alpha&b=bravo") - .build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description", "Foo").row("a", "one", "alpha").row("b", "two", "bravo")); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()).isTable((table) -> table.withHeader("Parameter", "Description", "Foo") + .row("a", "one", "alpha") + .row("b", "two", "bravo")); } - @Test - public void formParametersWithOptionalColumn() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("form-parameters")) - .willReturn(snippetResource("form-parameters-with-optional-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "form-parameters", template = "form-parameters-with-optional-column") + void formParametersWithOptionalColumn(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), parameterWithName("b").description("two"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") - .content("a=alpha&b=bravo") - .build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Optional", "Description").row("a", "true", "one") + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Optional", "Description") + .row("a", "true", "one") .row("b", "false", "two")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { RequestDocumentation.formParameters(parameterWithName("a").description("one")) .and(parameterWithName("b").description("two")) - .document(this.operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void additionalDescriptorsWithRelaxedFormParameters() throws IOException { + @RenderedSnippetTest + void additionalDescriptorsWithRelaxedFormParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { RequestDocumentation.relaxedFormParameters(parameterWithName("a").description("one")) .and(parameterWithName("b").description("two")) - .document(this.operationBuilder.request("http://localhost/") - .content("a=alpha&b=bravo&c=undocumented") - .build()); - assertThat(this.generatedSnippets.formParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").content("a=alpha&b=bravo&c=undocumented").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void formParametersWithEscapedContent() throws IOException { + @RenderedSnippetTest + void formParametersWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { RequestDocumentation.formParameters(parameterWithName("Foo|Bar").description("one|two")) - .document(this.operationBuilder.request("http://localhost/").content("Foo%7CBar=baz").build()); - assertThat(this.generatedSnippets.formParameters()).is(tableWithHeader("Parameter", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.request("http://localhost/").content("Foo%7CBar=baz").build()); + assertThat(snippets.formParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`Foo|Bar`", "one|two")); + } + + @SnippetTest + void undocumentedParameter(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new FormParametersSnippet(Collections.emptyList()) + .document(operationBuilder.request("http://localhost/").content("a=alpha").build())) + .withMessage("Form parameters with the following names were not documented: [a]"); + } + + @SnippetTest + void missingParameter(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) + .document(operationBuilder.request("http://localhost/").build())) + .withMessage("Form parameters with the following names were not found in the request: [a]"); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void undocumentedAndMissingParameters(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) + .document(operationBuilder.request("http://localhost/").content("b=bravo").build())) + .withMessage("Form parameters with the following names were not documented: [b]. Form parameters" + + " with the following names were not found in the request: [a]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java deleted file mode 100644 index b8c7d374..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetFailureTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; - -/** - * Tests for failures when rendering {@link PathParametersSnippet} due to missing or - * undocumented path parameters. - * - * @author Andy Wilkinson - */ -public class PathParametersSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedPathParameter() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new PathParametersSnippet(Collections.emptyList()).document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/") - .build())) - .withMessage("Path parameters with the following names were not documented: [a]"); - } - - @Test - public void missingPathParameter() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/") - .build())) - .withMessage("Path parameters with the following names were not found in the request: [a]"); - } - - @Test - public void undocumentedAndMissingPathParameters() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{b}") - .build())) - .withMessage("Path parameters with the following names were not documented: [b]. Path parameters with the" - + " following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java index 6a0009a1..d178bde5 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,20 +18,20 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.templates.TemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; import org.springframework.restdocs.templates.TemplateFormat; import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -41,164 +41,192 @@ * * @author Andy Wilkinson */ -public class PathParametersSnippetTests extends AbstractSnippetTests { - - public PathParametersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class PathParametersSnippetTests { - @Test - public void pathParameters() throws IOException { + @RenderedSnippetTest + void pathParameters(OperationBuilder operationBuilder, AssertableSnippets snippets, TemplateFormat templateFormat) + throws IOException { new PathParametersSnippet( Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") + .build()); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") + .row("`a`", "one") + .row("`b`", "two")); } - @Test - public void ignoredPathParameter() throws IOException { + @RenderedSnippetTest + void ignoredPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet( Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("`b`", "two")); + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") + .build()); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") + .row("`b`", "two")); } - @Test - public void allUndocumentedPathParametersCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedPathParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true).document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("`b`", "two")); + operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}").build()); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") + .row("`b`", "two")); } - @Test - public void missingOptionalPathParameter() throws IOException { + @RenderedSnippetTest + void missingOptionalPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two").optional())) - .document(this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle("/{a}"), "Parameter", "Description").row("`a`", "one") + .document( + operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}").build()); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat, "/{a}"), "Parameter", "Description") + .row("`a`", "one") .row("`b`", "two")); } - @Test - public void presentOptionalPathParameter() throws IOException { - new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())) - .document(this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle("/{a}"), "Parameter", "Description").row("`a`", "one")); + @RenderedSnippetTest + void presentOptionalPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { + new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())).document( + operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}").build()); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat, "/{a}"), "Parameter", "Description") + .row("`a`", "one")); } - @Test - public void pathParametersWithQueryString() throws IOException { + @RenderedSnippetTest + void pathParametersWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet( Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(this.operationBuilder + .document(operationBuilder .attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}?foo=bar") .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("`a`", "one").row("`b`", "two")); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") + .row("`a`", "one") + .row("`b`", "two")); } - @Test - public void pathParametersWithQueryStringWithParameters() throws IOException { + @RenderedSnippetTest + void pathParametersWithQueryStringWithParameters(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet( Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(this.operationBuilder + .document(operationBuilder .attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}?foo={c}") .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("`a`", "one").row("`b`", "two")); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") + .row("`a`", "one") + .row("`b`", "two")); } - @Test - public void pathParametersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("path-parameters")) - .willReturn(snippetResource("path-parameters-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "path-parameters", template = "path-parameters-with-title") + void pathParametersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet( Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), parameterWithName("b").description("two").attributes(key("foo").value("bravo"))), attributes(key("title").value("The title"))) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .build()); - assertThat(this.generatedSnippets.pathParameters()).contains("The title"); + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") + .build()); + assertThat(snippets.pathParameters()).contains("The title"); } - @Test - public void pathParametersWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("path-parameters")) - .willReturn(snippetResource("path-parameters-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "path-parameters", template = "path-parameters-with-extra-column") + void pathParametersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { new PathParametersSnippet( Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), parameterWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithHeader("Parameter", "Description", "Foo").row("a", "one", "alpha").row("b", "two", "bravo")); + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") + .build()); + assertThat(snippets.pathParameters()).isTable((table) -> table.withHeader("Parameter", "Description", "Foo") + .row("a", "one", "alpha") + .row("b", "two", "bravo")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { RequestDocumentation.pathParameters(parameterWithName("a").description("one")) .and(parameterWithName("b").description("two")) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle(), "Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") + .build()); + assertThat(snippets.pathParameters()).isTable( + (table) -> table.withTitleAndHeader(getTitle(templateFormat, "/{a}/{b}"), "Parameter", "Description") + .row("`a`", "one") + .row("`b`", "two")); } - @Test - public void additionalDescriptorsWithRelaxedRequestParameters() throws IOException { + @RenderedSnippetTest + void additionalDescriptorsWithRelaxedRequestParameters(OperationBuilder operationBuilder, + AssertableSnippets snippets, TemplateFormat templateFormat) throws IOException { RequestDocumentation.relaxedPathParameters(parameterWithName("a").description("one")) .and(parameterWithName("b").description("two")) - .document(this.operationBuilder - .attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}/{c}") + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}/{c}") .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle("/{a}/{b}/{c}"), "Parameter", "Description").row("`a`", "one") - .row("`b`", "two")); + assertThat(snippets.pathParameters()).isTable((table) -> table + .withTitleAndHeader(getTitle(templateFormat, "/{a}/{b}/{c}"), "Parameter", "Description") + .row("`a`", "one") + .row("`b`", "two")); } - @Test - public void pathParametersWithEscapedContent() throws IOException { + @RenderedSnippetTest + void pathParametersWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { RequestDocumentation.pathParameters(parameterWithName("Foo|Bar").description("one|two")) - .document( - this.operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{Foo|Bar}") - .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle("{Foo|Bar}"), "Parameter", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{Foo|Bar}") + .build()); + assertThat(snippets.pathParameters()).isTable( + (table) -> table.withTitleAndHeader(getTitle(templateFormat, "{Foo|Bar}"), "Parameter", "Description") + .row("`Foo|Bar`", "one|two")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void undocumentedPathParameter(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new PathParametersSnippet(Collections.emptyList()) + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/") + .build())) + .withMessage("Path parameters with the following names were not documented: [a]"); + } + + @SnippetTest + void missingPathParameter(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/") + .build())) + .withMessage("Path parameters with the following names were not found in the request: [a]"); + } + + @SnippetTest + void undocumentedAndMissingPathParameters(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{b}") + .build())) + .withMessage("Path parameters with the following names were not documented: [b]. Path parameters with the" + + " following names were not found in the request: [a]"); } - private String getTitle() { - return getTitle("/{a}/{b}"); + private String getTitle(TemplateFormat templateFormat) { + return getTitle(templateFormat, "/{a}/{b}"); } - private String getTitle(String title) { - if (this.templateFormat.getId().equals(TemplateFormats.asciidoctor().getId())) { + private String getTitle(TemplateFormat templateFormat, String title) { + if (templateFormat.getId().equals(TemplateFormats.asciidoctor().getId())) { return "+" + title + "+"; } return "`" + title + "`"; diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetFailureTests.java deleted file mode 100644 index 2590575d..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetFailureTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; - -/** - * Tests for failures when rendering {@link QueryParametersSnippet} due to missing or - * undocumented query parameters. - * - * @author Andy Wilkinson - */ -public class QueryParametersSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedParameter() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new QueryParametersSnippet(Collections.emptyList()) - .document(this.operationBuilder.request("http://localhost/?a=alpha").build())) - .withMessage("Query parameters with the following names were not documented: [a]"); - } - - @Test - public void missingParameter() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").build())) - .withMessage("Query parameters with the following names were not found in the request: [a]"); - } - - @Test - public void undocumentedAndMissingParameters() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/?b=bravo").build())) - .withMessage("Query parameters with the following names were not documented: [b]. Query parameters" - + " with the following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java index 2398f988..8084a238 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,17 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -40,142 +38,151 @@ * * @author Andy Wilkinson */ -public class QueryParametersSnippetTests extends AbstractSnippetTests { - - public QueryParametersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class QueryParametersSnippetTests { - @Test - public void queryParameters() throws IOException { + @RenderedSnippetTest + void queryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new QueryParametersSnippet( Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void queryParameterWithNoValue() throws IOException { + @RenderedSnippetTest + void queryParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/?a").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one")); + .document(operationBuilder.request("http://localhost/?a").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); } - @Test - public void ignoredQueryParameter() throws IOException { + @RenderedSnippetTest + void ignoredQueryParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new QueryParametersSnippet( Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); } - @Test - public void allUndocumentedQueryParametersCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedQueryParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new QueryParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true) - .document(this.operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); } - @Test - public void missingOptionalQueryParameter() throws IOException { + @RenderedSnippetTest + void missingOptionalQueryParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), parameterWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/?b=bravo").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/?b=bravo").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void presentOptionalQueryParameter() throws IOException { + @RenderedSnippetTest + void presentOptionalQueryParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())) - .document(this.operationBuilder.request("http://localhost/?a=alpha").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one")); + .document(operationBuilder.request("http://localhost/?a=alpha").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); } - @Test - public void queryParametersWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("query-parameters")) - .willReturn(snippetResource("query-parameters-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "query-parameters", template = "query-parameters-with-title") + void queryParametersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new QueryParametersSnippet( Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), parameterWithName("b").description("two").attributes(key("foo").value("bravo"))), attributes(key("title").value("The title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/?a=alpha&b=bravo") - .build()); - assertThat(this.generatedSnippets.queryParameters()).contains("The title"); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()).contains("The title"); } - @Test - public void queryParametersWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("query-parameters")) - .willReturn(snippetResource("query-parameters-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "query-parameters", template = "query-parameters-with-extra-column") + void queryParametersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new QueryParametersSnippet( Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), parameterWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/?a=alpha&b=bravo") - .build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description", "Foo").row("a", "one", "alpha").row("b", "two", "bravo")); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()).isTable((table) -> table.withHeader("Parameter", "Description", "Foo") + .row("a", "one", "alpha") + .row("b", "two", "bravo")); } - @Test - public void queryParametersWithOptionalColumn() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("query-parameters")) - .willReturn(snippetResource("query-parameters-with-optional-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "query-parameters", template = "query-parameters-with-optional-column") + void queryParametersWithOptionalColumn(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), parameterWithName("b").description("two"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/?a=alpha&b=bravo") - .build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Optional", "Description").row("a", "true", "one") + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Optional", "Description") + .row("a", "true", "one") .row("b", "false", "two")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { RequestDocumentation.queryParameters(parameterWithName("a").description("one")) .and(parameterWithName("b").description("two")) - .document(this.operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void additionalDescriptorsWithRelaxedQueryParameters() throws IOException { + @RenderedSnippetTest + void additionalDescriptorsWithRelaxedQueryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { RequestDocumentation.relaxedQueryParameters(parameterWithName("a").description("one")) .and(parameterWithName("b").description("two")) - .document(this.operationBuilder.request("http://localhost/?a=alpha&b=bravo&c=undocumented").build()); - assertThat(this.generatedSnippets.queryParameters()) - .is(tableWithHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/?a=alpha&b=bravo&c=undocumented").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void queryParametersWithEscapedContent() throws IOException { + @RenderedSnippetTest + void queryParametersWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { RequestDocumentation.queryParameters(parameterWithName("Foo|Bar").description("one|two")) - .document(this.operationBuilder.request("http://localhost/?Foo%7CBar=baz").build()); - assertThat(this.generatedSnippets.queryParameters()).is(tableWithHeader("Parameter", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.request("http://localhost/?Foo%7CBar=baz").build()); + assertThat(snippets.queryParameters()) + .isTable((table) -> table.withHeader("Parameter", "Description").row("`Foo|Bar`", "one|two")); + } + + @SnippetTest + void undocumentedParameter(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new QueryParametersSnippet(Collections.emptyList()) + .document(operationBuilder.request("http://localhost/?a=alpha").build())) + .withMessage("Query parameters with the following names were not documented: [a]"); + } + + @SnippetTest + void missingParameter(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) + .document(operationBuilder.request("http://localhost/").build())) + .withMessage("Query parameters with the following names were not found in the request: [a]"); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void undocumentedAndMissingParameters(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) + .document(operationBuilder.request("http://localhost/?b=bravo").build())) + .withMessage("Query parameters with the following names were not documented: [b]. Query parameters" + + " with the following names were not found in the request: [a]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetFailureTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetFailureTests.java deleted file mode 100644 index e8ad1587..00000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetFailureTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Rule; -import org.junit.Test; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.OperationBuilder; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; - -/** - * Tests for failures when rendering {@link RequestPartsSnippet} due to missing or - * undocumented request parts. - * - * @author Andy Wilkinson - */ -public class RequestPartsSnippetFailureTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Test - public void undocumentedPart() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartsSnippet(Collections.emptyList()) - .document(this.operationBuilder.request("http://localhost/").part("a", "alpha".getBytes()).build())) - .withMessage("Request parts with the following names were not documented: [a]"); - } - - @Test - public void missingPart() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").build())) - .withMessage("Request parts with the following names were not found in the request: [a]"); - } - - @Test - public void undocumentedAndMissingParts() { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"))) - .document(this.operationBuilder.request("http://localhost/").part("b", "bravo".getBytes()).build())) - .withMessage("Request parts with the following names were not documented: [b]. Request parts with the" - + " following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java index 51c3fbb0..9dcc12f9 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,19 +18,17 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; -import org.junit.Test; - -import org.springframework.restdocs.AbstractSnippetTests; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.snippet.SnippetException; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; +import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.restdocs.request.RequestDocumentation.partWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -40,145 +38,157 @@ * * @author Andy Wilkinson */ -public class RequestPartsSnippetTests extends AbstractSnippetTests { - - public RequestPartsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } +class RequestPartsSnippetTests { - @Test - public void requestParts() throws IOException { + @RenderedSnippetTest + void requestParts(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestPartsSnippet( Arrays.asList(partWithName("a").description("one"), partWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "bravo".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()) - .is(tableWithHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void ignoredRequestPart() throws IOException { + @RenderedSnippetTest + void ignoredRequestPart(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestPartsSnippet(Arrays.asList(partWithName("a").ignored(), partWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "bravo".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()).is(tableWithHeader("Part", "Description").row("`b`", "two")); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`b`", "two")); } - @Test - public void allUndocumentedRequestPartsCanBeIgnored() throws IOException { + @RenderedSnippetTest + void allUndocumentedRequestPartsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestPartsSnippet(Arrays.asList(partWithName("b").description("two")), true) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "bravo".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()).is(tableWithHeader("Part", "Description").row("`b`", "two")); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`b`", "two")); } - @Test - public void missingOptionalRequestPart() throws IOException { + @RenderedSnippetTest + void missingOptionalRequestPart(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestPartsSnippet( Arrays.asList(partWithName("a").description("one").optional(), partWithName("b").description("two"))) - .document(this.operationBuilder.request("http://localhost/").part("b", "bravo".getBytes()).build()); - assertThat(this.generatedSnippets.requestParts()) - .is(tableWithHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); + .document(operationBuilder.request("http://localhost/").part("b", "bravo".getBytes()).build()); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void presentOptionalRequestPart() throws IOException { + @RenderedSnippetTest + void presentOptionalRequestPart(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one").optional())) - .document(this.operationBuilder.request("http://localhost/").part("a", "one".getBytes()).build()); - assertThat(this.generatedSnippets.requestParts()).is(tableWithHeader("Part", "Description").row("`a`", "one")); + .document(operationBuilder.request("http://localhost/").part("a", "one".getBytes()).build()); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one")); } - @Test - public void requestPartsWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-parts")) - .willReturn(snippetResource("request-parts-with-title")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-parts", template = "request-parts-with-title") + void requestPartsWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestPartsSnippet( Arrays.asList(partWithName("a").description("one").attributes(key("foo").value("alpha")), partWithName("b").description("two").attributes(key("foo").value("bravo"))), attributes(key("title").value("The title"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "alpha".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()).contains("The title"); + assertThat(snippets.requestParts()).contains("The title"); } - @Test - public void requestPartsWithCustomDescriptorAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-parts")) - .willReturn(snippetResource("request-parts-with-extra-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-parts", template = "request-parts-with-extra-column") + void requestPartsWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestPartsSnippet( Arrays.asList(partWithName("a").description("one").attributes(key("foo").value("alpha")), partWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "alpha".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()) - .is(tableWithHeader("Part", "Description", "Foo").row("a", "one", "alpha").row("b", "two", "bravo")); + assertThat(snippets.requestParts()).isTable((table) -> table.withHeader("Part", "Description", "Foo") + .row("a", "one", "alpha") + .row("b", "two", "bravo")); } - @Test - public void requestPartsWithOptionalColumn() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-parts")) - .willReturn(snippetResource("request-parts-with-optional-column")); + @RenderedSnippetTest + @SnippetTemplate(snippet = "request-parts", template = "request-parts-with-optional-column") + void requestPartsWithOptionalColumn(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { new RequestPartsSnippet( Arrays.asList(partWithName("a").description("one").optional(), partWithName("b").description("two"))) - .document(this.operationBuilder - .attribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver)) - .request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "alpha".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()) - .is(tableWithHeader("Part", "Optional", "Description").row("a", "true", "one").row("b", "false", "two")); + assertThat(snippets.requestParts()).isTable((table) -> table.withHeader("Part", "Optional", "Description") + .row("a", "true", "one") + .row("b", "false", "two")); } - @Test - public void additionalDescriptors() throws IOException { + @RenderedSnippetTest + void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { RequestDocumentation.requestParts(partWithName("a").description("one")) .and(partWithName("b").description("two")) - .document(this.operationBuilder.request("http://localhost/") + .document(operationBuilder.request("http://localhost/") .part("a", "bravo".getBytes()) .and() .part("b", "bravo".getBytes()) .build()); - assertThat(this.generatedSnippets.requestParts()) - .is(tableWithHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); } - @Test - public void requestPartsWithEscapedContent() throws IOException { + @RenderedSnippetTest + void requestPartsWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { RequestDocumentation.requestParts(partWithName("Foo|Bar").description("one|two")) - .document(this.operationBuilder.request("http://localhost/").part("Foo|Bar", "baz".getBytes()).build()); - assertThat(this.generatedSnippets.requestParts()).is(tableWithHeader("Part", "Description") - .row(escapeIfNecessary("`Foo|Bar`"), escapeIfNecessary("one|two"))); + .document(operationBuilder.request("http://localhost/").part("Foo|Bar", "baz".getBytes()).build()); + assertThat(snippets.requestParts()) + .isTable((table) -> table.withHeader("Part", "Description").row("`Foo|Bar`", "one|two")); + } + + @SnippetTest + void undocumentedPart(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestPartsSnippet(Collections.emptyList()) + .document(operationBuilder.request("http://localhost/").part("a", "alpha".getBytes()).build())) + .withMessage("Request parts with the following names were not documented: [a]"); + } + + @SnippetTest + void missingPart(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"))) + .document(operationBuilder.request("http://localhost/").build())) + .withMessage("Request parts with the following names were not found in the request: [a]"); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @SnippetTest + void undocumentedAndMissingParts(OperationBuilder operationBuilder) { + assertThatExceptionOfType(SnippetException.class) + .isThrownBy(() -> new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"))) + .document(operationBuilder.request("http://localhost/").part("b", "bravo".getBytes()).build())) + .withMessage("Request parts with the following names were not documented: [b]. Request parts with the" + + " following names were not found in the request: [a]"); } } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java index ab592d38..22b285c3 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,7 +16,7 @@ package org.springframework.restdocs.snippet; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.ManualRestDocumentation; import org.springframework.restdocs.RestDocumentationContext; @@ -30,82 +30,82 @@ * @author Andy Wilkinson * */ -public class RestDocumentationContextPlaceholderResolverTests { +class RestDocumentationContextPlaceholderResolverTests { @Test - public void kebabCaseMethodName() { + void kebabCaseMethodName() { assertThat(createResolver("dashSeparatedMethodName").resolvePlaceholder("method-name")) .isEqualTo("dash-separated-method-name"); } @Test - public void kebabCaseMethodNameWithUpperCaseOpeningSection() { + void kebabCaseMethodNameWithUpperCaseOpeningSection() { assertThat(createResolver("URIDashSeparatedMethodName").resolvePlaceholder("method-name")) .isEqualTo("uri-dash-separated-method-name"); } @Test - public void kebabCaseMethodNameWithUpperCaseMidSection() { + void kebabCaseMethodNameWithUpperCaseMidSection() { assertThat(createResolver("dashSeparatedMethodNameWithURIInIt").resolvePlaceholder("method-name")) .isEqualTo("dash-separated-method-name-with-uri-in-it"); } @Test - public void kebabCaseMethodNameWithUpperCaseEndSection() { + void kebabCaseMethodNameWithUpperCaseEndSection() { assertThat(createResolver("dashSeparatedMethodNameWithURI").resolvePlaceholder("method-name")) .isEqualTo("dash-separated-method-name-with-uri"); } @Test - public void snakeCaseMethodName() { + void snakeCaseMethodName() { assertThat(createResolver("underscoreSeparatedMethodName").resolvePlaceholder("method_name")) .isEqualTo("underscore_separated_method_name"); } @Test - public void snakeCaseMethodNameWithUpperCaseOpeningSection() { + void snakeCaseMethodNameWithUpperCaseOpeningSection() { assertThat(createResolver("URIUnderscoreSeparatedMethodName").resolvePlaceholder("method_name")) .isEqualTo("uri_underscore_separated_method_name"); } @Test - public void snakeCaseMethodNameWithUpperCaseMidSection() { + void snakeCaseMethodNameWithUpperCaseMidSection() { assertThat(createResolver("underscoreSeparatedMethodNameWithURIInIt").resolvePlaceholder("method_name")) .isEqualTo("underscore_separated_method_name_with_uri_in_it"); } @Test - public void snakeCaseMethodNameWithUpperCaseEndSection() { + void snakeCaseMethodNameWithUpperCaseEndSection() { assertThat(createResolver("underscoreSeparatedMethodNameWithURI").resolvePlaceholder("method_name")) .isEqualTo("underscore_separated_method_name_with_uri"); } @Test - public void camelCaseMethodName() { + void camelCaseMethodName() { assertThat(createResolver("camelCaseMethodName").resolvePlaceholder("methodName")) .isEqualTo("camelCaseMethodName"); } @Test - public void kebabCaseClassName() { + void kebabCaseClassName() { assertThat(createResolver().resolvePlaceholder("class-name")) .isEqualTo("rest-documentation-context-placeholder-resolver-tests"); } @Test - public void snakeCaseClassName() { + void snakeCaseClassName() { assertThat(createResolver().resolvePlaceholder("class_name")) .isEqualTo("rest_documentation_context_placeholder_resolver_tests"); } @Test - public void camelCaseClassName() { + void camelCaseClassName() { assertThat(createResolver().resolvePlaceholder("ClassName")) .isEqualTo("RestDocumentationContextPlaceholderResolverTests"); } @Test - public void stepCount() { + void stepCount() { assertThat(createResolver("stepCount").resolvePlaceholder("step")).isEqualTo("1"); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java index e4f600be..13f3b6cd 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -21,9 +21,8 @@ import java.io.IOException; import java.io.Writer; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.springframework.restdocs.ManualRestDocumentation; import org.springframework.restdocs.RestDocumentationContext; @@ -40,10 +39,10 @@ * * @author Andy Wilkinson */ -public class StandardWriterResolverTests { +class StandardWriterResolverTests { - @Rule - public final TemporaryFolder temp = new TemporaryFolder(); + @TempDir + File temp; private final PlaceholderResolverFactory placeholderResolverFactory = mock(PlaceholderResolverFactory.class); @@ -51,21 +50,21 @@ public class StandardWriterResolverTests { TemplateFormats.asciidoctor()); @Test - public void absoluteInput() { + void absoluteInput() { String absolutePath = new File("foo").getAbsolutePath(); assertThat(this.resolver.resolveFile(absolutePath, "bar.txt", createContext(absolutePath))) .isEqualTo(new File(absolutePath, "bar.txt")); } @Test - public void configuredOutputAndRelativeInput() { + void configuredOutputAndRelativeInput() { File outputDir = new File("foo").getAbsoluteFile(); assertThat(this.resolver.resolveFile("bar", "baz.txt", createContext(outputDir.getAbsolutePath()))) .isEqualTo(new File(outputDir, "bar/baz.txt")); } @Test - public void configuredOutputAndAbsoluteInput() { + void configuredOutputAndAbsoluteInput() { File outputDir = new File("foo").getAbsoluteFile(); String absolutePath = new File("bar").getAbsolutePath(); assertThat(this.resolver.resolveFile(absolutePath, "baz.txt", createContext(outputDir.getAbsolutePath()))) @@ -73,8 +72,8 @@ public void configuredOutputAndAbsoluteInput() { } @Test - public void placeholdersAreResolvedInOperationName() throws IOException { - File outputDirectory = this.temp.newFolder(); + void placeholdersAreResolvedInOperationName() throws IOException { + File outputDirectory = this.temp; RestDocumentationContext context = createContext(outputDirectory.getAbsolutePath()); PlaceholderResolver resolver = mock(PlaceholderResolver.class); given(resolver.resolvePlaceholder("a")).willReturn("alpha"); @@ -84,8 +83,8 @@ public void placeholdersAreResolvedInOperationName() throws IOException { } @Test - public void placeholdersAreResolvedInSnippetName() throws IOException { - File outputDirectory = this.temp.newFolder(); + void placeholdersAreResolvedInSnippetName() throws IOException { + File outputDirectory = this.temp; RestDocumentationContext context = createContext(outputDirectory.getAbsolutePath()); PlaceholderResolver resolver = mock(PlaceholderResolver.class); given(resolver.resolvePlaceholder("b")).willReturn("bravo"); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java index 8892891a..724e68a5 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2025 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. @@ -21,13 +21,12 @@ import java.util.HashMap; import java.util.Map; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.GeneratedSnippets; -import org.springframework.restdocs.testfixtures.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; +import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; import static org.assertj.core.api.Assertions.assertThat; @@ -36,16 +35,10 @@ * * @author Andy Wilkinson */ -public class TemplatedSnippetTests { - - @Rule - public OperationBuilder operationBuilder = new OperationBuilder(TemplateFormats.asciidoctor()); - - @Rule - public GeneratedSnippets snippets = new GeneratedSnippets(TemplateFormats.asciidoctor()); +class TemplatedSnippetTests { @Test - public void attributesAreCopied() { + void attributesAreCopied() { Map attributes = new HashMap<>(); attributes.put("a", "alpha"); TemplatedSnippet snippet = new TestTemplatedSnippet(attributes); @@ -55,22 +48,23 @@ public void attributesAreCopied() { } @Test - public void nullAttributesAreTolerated() { + void nullAttributesAreTolerated() { assertThat(new TestTemplatedSnippet(null).getAttributes()).isNotNull(); assertThat(new TestTemplatedSnippet(null).getAttributes()).isEmpty(); } @Test - public void snippetName() { + void snippetName() { assertThat(new TestTemplatedSnippet(Collections.emptyMap()).getSnippetName()).isEqualTo("test"); } - @Test - public void multipleSnippetsCanBeProducedFromTheSameTemplate() throws IOException { - new TestTemplatedSnippet("one", "multiple-snippets").document(this.operationBuilder.build()); - new TestTemplatedSnippet("two", "multiple-snippets").document(this.operationBuilder.build()); - assertThat(this.snippets.snippet("multiple-snippets-one")).isNotNull(); - assertThat(this.snippets.snippet("multiple-snippets-two")).isNotNull(); + @RenderedSnippetTest + void multipleSnippetsCanBeProducedFromTheSameTemplate(OperationBuilder operationBuilder, AssertableSnippets snippet) + throws IOException { + new TestTemplatedSnippet("one", "multiple-snippets").document(operationBuilder.build()); + new TestTemplatedSnippet("two", "multiple-snippets").document(operationBuilder.build()); + assertThat(snippet.named("multiple-snippets-one")).exists(); + assertThat(snippet.named("multiple-snippets-two")).exists(); } private static class TestTemplatedSnippet extends TemplatedSnippet { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java index f7d0c9f7..4f564633 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -22,7 +22,7 @@ import java.util.Map; import java.util.concurrent.Callable; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.core.io.Resource; @@ -34,7 +34,7 @@ * * @author Andy Wilkinson */ -public class StandardTemplateResourceResolverTests { +class StandardTemplateResourceResolverTests { private final TemplateResourceResolver resolver = new StandardTemplateResourceResolver( TemplateFormats.asciidoctor()); @@ -42,7 +42,7 @@ public class StandardTemplateResourceResolverTests { private final TestClassLoader classLoader = new TestClassLoader(); @Test - public void formatSpecificCustomSnippetHasHighestPrecedence() throws IOException { + void formatSpecificCustomSnippetHasHighestPrecedence() throws IOException { this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/test.snippet", getClass().getResource("test-format-specific-custom.snippet")); this.classLoader.addResource("org/springframework/restdocs/templates/test.snippet", @@ -62,7 +62,7 @@ public Resource call() { } @Test - public void generalCustomSnippetIsUsedInAbsenceOfFormatSpecificCustomSnippet() throws IOException { + void generalCustomSnippetIsUsedInAbsenceOfFormatSpecificCustomSnippet() throws IOException { this.classLoader.addResource("org/springframework/restdocs/templates/test.snippet", getClass().getResource("test-custom.snippet")); this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/default-test.snippet", @@ -80,7 +80,7 @@ public Resource call() { } @Test - public void defaultSnippetIsUsedInAbsenceOfCustomSnippets() throws Exception { + void defaultSnippetIsUsedInAbsenceOfCustomSnippets() throws Exception { this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/default-test.snippet", getClass().getResource("test-default.snippet")); Resource snippet = doWithThreadContextClassLoader(this.classLoader, new Callable() { @@ -96,7 +96,7 @@ public Resource call() { } @Test - public void failsIfCustomAndDefaultSnippetsDoNotExist() { + void failsIfCustomAndDefaultSnippetsDoNotExist() { assertThatIllegalStateException() .isThrownBy(() -> doWithThreadContextClassLoader(this.classLoader, () -> StandardTemplateResourceResolverTests.this.resolver.resolveTemplateResource("test"))) diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java index 161471d0..635a80ae 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2018 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.io.IOException; import java.io.StringWriter; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.mustache.Template.Fragment; @@ -32,10 +32,10 @@ * * @author Andy Wilkinson */ -public class AsciidoctorTableCellContentLambdaTests { +class AsciidoctorTableCellContentLambdaTests { @Test - public void verticalBarCharactersAreEscaped() throws IOException { + void verticalBarCharactersAreEscaped() throws IOException { Fragment fragment = mock(Fragment.class); given(fragment.execute()).willReturn("|foo|bar|baz|"); StringWriter writer = new StringWriter(); @@ -44,7 +44,7 @@ public void verticalBarCharactersAreEscaped() throws IOException { } @Test - public void escapedVerticalBarCharactersAreNotEscapedAgain() throws IOException { + void escapedVerticalBarCharactersAreNotEscapedAgain() throws IOException { Fragment fragment = mock(Fragment.class); given(fragment.execute()).willReturn("\\|foo|bar\\|baz|"); StringWriter writer = new StringWriter(); diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet index 24bb63fa..ff141ad3 100644 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet +++ b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet @@ -1,4 +1,5 @@ {{title}} + Path | Type | Description ---- | ---- | ----------- {{#fields}} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/GeneratedSnippets.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/GeneratedSnippets.java deleted file mode 100644 index a00d0d09..00000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/GeneratedSnippets.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; - -import org.junit.runners.model.Statement; - -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.util.FileCopyUtils; - -import static org.assertj.core.api.Assertions.fail; - -/** - * The {@code GeneratedSnippets} rule is used to capture the snippets generated by a test - * and assert their existence and content. - * - * @author Andy Wilkinson - * @author Andreas Evers - */ -public class GeneratedSnippets extends OperationTestRule { - - private final TemplateFormat templateFormat; - - private String operationName; - - private File outputDirectory; - - public GeneratedSnippets(TemplateFormat templateFormat) { - this.templateFormat = templateFormat; - } - - @Override - public Statement apply(Statement base, File outputDirectory, String operationName) { - this.outputDirectory = outputDirectory; - this.operationName = operationName; - return base; - } - - public String curlRequest() { - return snippet("curl-request"); - } - - public String httpieRequest() { - return snippet("httpie-request"); - } - - public String requestHeaders() { - return snippet("request-headers"); - } - - public String responseHeaders() { - return snippet("response-headers"); - } - - public String requestCookies() { - return snippet("request-cookies"); - } - - public String responseCookies() { - return snippet("response-cookies"); - } - - public String httpRequest() { - return snippet("http-request"); - } - - public String httpResponse() { - return snippet("http-response"); - } - - public String links() { - return snippet("links"); - } - - public String requestFields() { - return snippet("request-fields"); - } - - public String requestParts() { - return snippet("request-parts"); - } - - public String requestPartFields(String partName) { - return snippet("request-part-" + partName + "-fields"); - } - - public String responseFields() { - return snippet("response-fields"); - } - - public String pathParameters() { - return snippet("path-parameters"); - } - - public String queryParameters() { - return snippet("query-parameters"); - } - - public String formParameters() { - return snippet("form-parameters"); - } - - public String snippet(String name) { - File snippetFile = getSnippetFile(name); - try { - return FileCopyUtils - .copyToString(new InputStreamReader(new FileInputStream(snippetFile), StandardCharsets.UTF_8)); - } - catch (Exception ex) { - fail("Failed to read '" + snippetFile + "'", ex); - return null; - } - } - - private File getSnippetFile(String name) { - if (this.outputDirectory == null) { - fail("Output directory was null"); - } - if (this.operationName == null) { - fail("Operation name was null"); - } - File snippetDir = new File(this.outputDirectory, this.operationName); - return new File(snippetDir, name + "." + this.templateFormat.getFileExtension()); - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationTestRule.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationTestRule.java deleted file mode 100644 index 79e19f0f..00000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationTestRule.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2021 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures; - -import java.io.File; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -/** - * Abstract base class for Operation-related {@link TestRule TestRules}. - * - * @author Andy Wilkinson - */ -abstract class OperationTestRule implements TestRule { - - @Override - public final Statement apply(Statement base, Description description) { - return apply(base, determineOutputDirectory(description), determineOperationName(description)); - } - - private File determineOutputDirectory(Description description) { - return new File("build/" + description.getTestClass().getSimpleName()); - } - - private String determineOperationName(Description description) { - String operationName = description.getMethodName(); - int index = operationName.indexOf('['); - if (index > 0) { - operationName = operationName.substring(0, index); - } - return operationName; - } - - protected abstract Statement apply(Statement base, File outputDirectory, String operationName); - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCaptureRule.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCaptureRule.java deleted file mode 100644 index 38710f25..00000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCaptureRule.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014-2022 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures; - -import org.junit.Rule; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -/** - * JUnit {@code @Rule} to capture output from System.out and System.err. - *

- * To use add as a {@link Rule @Rule}: - * - *

- * public class MyTest {
- *
- *     @Rule
- *     public OutputCaptureRule output = new OutputCaptureRule();
- *
- *     @Test
- *     public void test() {
- *         assertThat(output).contains("ok");
- *     }
- *
- * }
- * 
- * - * @author Phillip Webb - * @author Andy Wilkinson - */ -public class OutputCaptureRule implements TestRule, CapturedOutput { - - private final OutputCapture delegate = new OutputCapture(); - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - OutputCaptureRule.this.delegate.push(); - try { - base.evaluate(); - } - finally { - OutputCaptureRule.this.delegate.pop(); - } - } - }; - } - - @Override - public String getAll() { - return this.delegate.getAll(); - } - - @Override - public String getOut() { - return this.delegate.getOut(); - } - - @Override - public String getErr() { - return this.delegate.getErr(); - } - - @Override - public String toString() { - return this.delegate.toString(); - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java new file mode 100644 index 00000000..fdab049e --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java @@ -0,0 +1,679 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.UnaryOperator; + +import org.assertj.core.api.AbstractStringAssert; +import org.assertj.core.api.AssertProvider; +import org.assertj.core.api.Assertions; + +import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.restdocs.templates.TemplateFormats; +import org.springframework.util.StringUtils; + +/** + * AssertJ {@link AssertProvider} for asserting that the generated snippets are correct. + * + * @author Andy Wilkinson + */ +public class AssertableSnippets { + + private final File outputDirectory; + + private final String operationName; + + private final TemplateFormat templateFormat; + + AssertableSnippets(File outputDirectory, String operationName, TemplateFormat templateFormat) { + this.outputDirectory = outputDirectory; + this.operationName = operationName; + this.templateFormat = templateFormat; + } + + public File named(String name) { + return getSnippetFile(name); + } + + private File getSnippetFile(String name) { + File snippetDir = new File(this.outputDirectory, this.operationName); + return new File(snippetDir, name + "." + this.templateFormat.getFileExtension()); + } + + public CodeBlockSnippetAssertProvider curlRequest() { + return new CodeBlockSnippetAssertProvider("curl-request"); + } + + public TableSnippetAssertProvider formParameters() { + return new TableSnippetAssertProvider("form-parameters"); + } + + public CodeBlockSnippetAssertProvider httpieRequest() { + return new CodeBlockSnippetAssertProvider("httpie-request"); + } + + public HttpRequestSnippetAssertProvider httpRequest() { + return new HttpRequestSnippetAssertProvider("http-request"); + } + + public HttpResponseSnippetAssertProvider httpResponse() { + return new HttpResponseSnippetAssertProvider("http-response"); + } + + public TableSnippetAssertProvider links() { + return new TableSnippetAssertProvider("links"); + } + + public TableSnippetAssertProvider pathParameters() { + return new TableSnippetAssertProvider("path-parameters"); + } + + public TableSnippetAssertProvider queryParameters() { + return new TableSnippetAssertProvider("query-parameters"); + } + + public CodeBlockSnippetAssertProvider requestBody() { + return new CodeBlockSnippetAssertProvider("request-body"); + } + + public CodeBlockSnippetAssertProvider requestBody(String suffix) { + return new CodeBlockSnippetAssertProvider("request-body-%s".formatted(suffix)); + } + + public TableSnippetAssertProvider requestCookies() { + return new TableSnippetAssertProvider("request-cookies"); + } + + public TableSnippetAssertProvider requestCookies(String suffix) { + return new TableSnippetAssertProvider("request-cookies-%s".formatted(suffix)); + } + + public TableSnippetAssertProvider requestFields() { + return new TableSnippetAssertProvider("request-fields"); + } + + public TableSnippetAssertProvider requestFields(String suffix) { + return new TableSnippetAssertProvider("request-fields-%s".formatted(suffix)); + } + + public TableSnippetAssertProvider requestHeaders() { + return new TableSnippetAssertProvider("request-headers"); + } + + public TableSnippetAssertProvider requestHeaders(String suffix) { + return new TableSnippetAssertProvider("request-headers-%s".formatted(suffix)); + } + + public CodeBlockSnippetAssertProvider requestPartBody(String partName) { + return new CodeBlockSnippetAssertProvider("request-part-%s-body".formatted(partName)); + } + + public CodeBlockSnippetAssertProvider requestPartBody(String partName, String suffix) { + return new CodeBlockSnippetAssertProvider("request-part-%s-body-%s".formatted(partName, suffix)); + } + + public TableSnippetAssertProvider requestPartFields(String partName) { + return new TableSnippetAssertProvider("request-part-%s-fields".formatted(partName)); + } + + public TableSnippetAssertProvider requestPartFields(String partName, String suffix) { + return new TableSnippetAssertProvider("request-part-%s-fields-%s".formatted(partName, suffix)); + } + + public TableSnippetAssertProvider requestParts() { + return new TableSnippetAssertProvider("request-parts"); + } + + public CodeBlockSnippetAssertProvider responseBody() { + return new CodeBlockSnippetAssertProvider("response-body"); + } + + public CodeBlockSnippetAssertProvider responseBody(String suffix) { + return new CodeBlockSnippetAssertProvider("response-body-%s".formatted(suffix)); + } + + public TableSnippetAssertProvider responseCookies() { + return new TableSnippetAssertProvider("response-cookies"); + } + + public TableSnippetAssertProvider responseFields() { + return new TableSnippetAssertProvider("response-fields"); + } + + public TableSnippetAssertProvider responseFields(String suffix) { + return new TableSnippetAssertProvider("response-fields-%s".formatted(suffix)); + } + + public TableSnippetAssertProvider responseHeaders() { + return new TableSnippetAssertProvider("response-headers"); + } + + public final class TableSnippetAssertProvider implements AssertProvider { + + private final String snippetName; + + private TableSnippetAssertProvider(String snippetName) { + this.snippetName = snippetName; + } + + @Override + public TableSnippetAssert assertThat() { + try { + String content = Files + .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName + + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) + .toPath()); + return new TableSnippetAssert(content); + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + } + + public final class TableSnippetAssert extends AbstractStringAssert { + + private TableSnippetAssert(String actual) { + super(actual, TableSnippetAssert.class); + } + + public void isTable(UnaryOperator> tableOperator) { + Table table = tableOperator + .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) + ? new AsciidoctorTable() : new MarkdownTable()); + table.getLinesAsString(); + Assertions.assertThat(this.actual).isEqualTo(table.getLinesAsString()); + } + + } + + public abstract class Table> extends SnippetContent { + + public abstract T withHeader(String... columns); + + public abstract T withTitleAndHeader(String title, String... columns); + + public abstract T row(String... entries); + + public abstract T configuration(String string); + + } + + private final class AsciidoctorTable extends Table { + + @Override + public AsciidoctorTable withHeader(String... columns) { + return withTitleAndHeader("", columns); + } + + @Override + public AsciidoctorTable withTitleAndHeader(String title, String... columns) { + if (!title.isBlank()) { + this.addLine("." + title); + } + this.addLine("|==="); + String header = "|" + StringUtils.collectionToDelimitedString(Arrays.asList(columns), "|"); + this.addLine(header); + this.addLine(""); + this.addLine("|==="); + return this; + } + + @Override + public AsciidoctorTable row(String... entries) { + for (String entry : entries) { + this.addLine(-1, "|" + escapeEntry(entry)); + } + this.addLine(-1, ""); + return this; + } + + private String escapeEntry(String entry) { + entry = entry.replace("|", "\\|"); + if (entry.startsWith("`") && entry.endsWith("`")) { + return "`+" + entry.substring(1, entry.length() - 1) + "+`"; + } + return entry; + } + + @Override + public AsciidoctorTable configuration(String configuration) { + this.addLine(0, configuration); + return this; + } + + } + + private final class MarkdownTable extends Table { + + @Override + public MarkdownTable withHeader(String... columns) { + return withTitleAndHeader("", columns); + } + + @Override + public MarkdownTable withTitleAndHeader(String title, String... columns) { + if (StringUtils.hasText(title)) { + this.addLine(title); + this.addLine(""); + } + String header = StringUtils.collectionToDelimitedString(Arrays.asList(columns), " | "); + this.addLine(header); + List components = new ArrayList<>(); + for (String column : columns) { + StringBuilder dashes = new StringBuilder(); + for (int i = 0; i < column.length(); i++) { + dashes.append("-"); + } + components.add(dashes.toString()); + } + this.addLine(StringUtils.collectionToDelimitedString(components, " | ")); + this.addLine(""); + return this; + } + + @Override + public MarkdownTable row(String... entries) { + this.addLine(-1, StringUtils.collectionToDelimitedString(Arrays.asList(entries), " | ")); + return this; + } + + @Override + public MarkdownTable configuration(String configuration) { + throw new UnsupportedOperationException("Markdown tables do not support configuration"); + } + + } + + public final class CodeBlockSnippetAssertProvider implements AssertProvider { + + private final String snippetName; + + private CodeBlockSnippetAssertProvider(String snippetName) { + this.snippetName = snippetName; + } + + @Override + public CodeBlockSnippetAssert assertThat() { + try { + String content = Files + .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName + + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) + .toPath()); + return new CodeBlockSnippetAssert(content); + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + } + + public final class CodeBlockSnippetAssert extends AbstractStringAssert { + + private CodeBlockSnippetAssert(String actual) { + super(actual, CodeBlockSnippetAssert.class); + } + + public void isCodeBlock(UnaryOperator> codeBlockOperator) { + CodeBlock codeBlock = codeBlockOperator + .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) + ? new AsciidoctorCodeBlock() : new MarkdownCodeBlock()); + Assertions.assertThat(this.actual).isEqualTo(codeBlock.getLinesAsString()); + } + + } + + public abstract class CodeBlock> extends SnippetContent { + + public abstract T withLanguage(String language); + + public abstract T withOptions(String options); + + public abstract T withLanguageAndOptions(String language, String options); + + public abstract T content(String string); + + } + + private final class AsciidoctorCodeBlock extends CodeBlock { + + @Override + public AsciidoctorCodeBlock withLanguage(String language) { + addLine("[source,%s]".formatted(language)); + return this; + } + + @Override + public AsciidoctorCodeBlock withOptions(String options) { + addLine("[source,options=\"%s\"]".formatted(options)); + return this; + } + + @Override + public AsciidoctorCodeBlock withLanguageAndOptions(String language, String options) { + addLine("[source,%s,options=\"%s\"]".formatted(language, options)); + return this; + } + + @Override + public AsciidoctorCodeBlock content(String content) { + addLine("----"); + addLine(content); + addLine("----"); + return this; + } + + } + + private final class MarkdownCodeBlock extends CodeBlock { + + @Override + public MarkdownCodeBlock withLanguage(String language) { + addLine("```%s".formatted(language)); + return this; + } + + @Override + public MarkdownCodeBlock withOptions(String options) { + addLine("```"); + return this; + } + + @Override + public MarkdownCodeBlock withLanguageAndOptions(String language, String options) { + addLine("```%s".formatted(language)); + return this; + } + + @Override + public MarkdownCodeBlock content(String content) { + addLine(content); + addLine("```"); + return this; + } + + } + + public final class HttpRequestSnippetAssertProvider implements AssertProvider { + + private final String snippetName; + + private HttpRequestSnippetAssertProvider(String snippetName) { + this.snippetName = snippetName; + } + + @Override + public HttpRequestSnippetAssert assertThat() { + try { + String content = Files + .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName + + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) + .toPath()); + return new HttpRequestSnippetAssert(content); + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + } + + public final class HttpRequestSnippetAssert extends AbstractStringAssert { + + private HttpRequestSnippetAssert(String actual) { + super(actual, HttpRequestSnippetAssert.class); + } + + public void isHttpRequest(UnaryOperator> operator) { + HttpRequest codeBlock = operator + .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) + ? new AsciidoctorHttpRequest() : new MarkdownHttpRequest()); + Assertions.assertThat(this.actual).isEqualTo(codeBlock.getLinesAsString()); + } + + } + + public abstract class HttpRequest> extends SnippetContent { + + public T get(String uri) { + return request("GET", uri); + } + + public T post(String uri) { + return request("POST", uri); + } + + public T put(String uri) { + return request("PUT", uri); + } + + public T patch(String uri) { + return request("PATCH", uri); + } + + public T delete(String uri) { + return request("DELETE", uri); + } + + protected abstract T request(String method, String uri); + + public abstract T header(String name, Object value); + + @SuppressWarnings("unchecked") + public T content(String content) { + addLine(-1, content); + return (T) this; + } + + } + + private final class AsciidoctorHttpRequest extends HttpRequest { + + private int headerOffset = 3; + + @Override + protected AsciidoctorHttpRequest request(String method, String uri) { + addLine("[source,http,options=\"nowrap\"]"); + addLine("----"); + addLine("%s %s HTTP/1.1".formatted(method, uri)); + addLine(""); + addLine("----"); + return this; + } + + @Override + public AsciidoctorHttpRequest header(String name, Object value) { + addLine(this.headerOffset++, "%s: %s".formatted(name, value)); + return this; + } + + } + + private final class MarkdownHttpRequest extends HttpRequest { + + private int headerOffset = 2; + + @Override + public MarkdownHttpRequest request(String method, String uri) { + addLine("```http"); + addLine("%s %s HTTP/1.1".formatted(method, uri)); + addLine(""); + addLine("```"); + return this; + } + + @Override + public MarkdownHttpRequest header(String name, Object value) { + addLine(this.headerOffset++, "%s: %s".formatted(name, value)); + return this; + } + + } + + public final class HttpResponseSnippetAssertProvider implements AssertProvider { + + private final String snippetName; + + private HttpResponseSnippetAssertProvider(String snippetName) { + this.snippetName = snippetName; + } + + @Override + public HttpResponseSnippetAssert assertThat() { + try { + String content = Files + .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName + + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) + .toPath()); + return new HttpResponseSnippetAssert(content); + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + } + + public final class HttpResponseSnippetAssert extends AbstractStringAssert { + + private HttpResponseSnippetAssert(String actual) { + super(actual, HttpResponseSnippetAssert.class); + } + + public void isHttpResponse(UnaryOperator> operator) { + HttpResponse httpResponse = operator + .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) + ? new AsciidoctorHttpResponse() : new MarkdownHttpResponse()); + Assertions.assertThat(this.actual).isEqualTo(httpResponse.getLinesAsString()); + } + + } + + public abstract class HttpResponse> extends SnippetContent { + + public T ok() { + return status("200 OK"); + } + + public T badRequest() { + return status("400 Bad Request"); + } + + public T status(int status) { + return status("%d ".formatted(status)); + } + + protected abstract T status(String status); + + public abstract T header(String name, Object value); + + @SuppressWarnings("unchecked") + public T content(String content) { + addLine(-1, content); + return (T) this; + } + + } + + private final class AsciidoctorHttpResponse extends HttpResponse { + + private int headerOffset = 3; + + @Override + protected AsciidoctorHttpResponse status(String status) { + addLine("[source,http,options=\"nowrap\"]"); + addLine("----"); + addLine("HTTP/1.1 %s".formatted(status)); + addLine(""); + addLine("----"); + return this; + } + + @Override + public AsciidoctorHttpResponse header(String name, Object value) { + addLine(this.headerOffset++, "%s: %s".formatted(name, value)); + return this; + } + + } + + private final class MarkdownHttpResponse extends HttpResponse { + + private int headerOffset = 2; + + @Override + public MarkdownHttpResponse status(String status) { + addLine("```http"); + addLine("HTTP/1.1 %s".formatted(status)); + addLine(""); + addLine("```"); + return this; + } + + @Override + public MarkdownHttpResponse header(String name, Object value) { + addLine(this.headerOffset++, "%s: %s".formatted(name, value)); + return this; + } + + } + + private static class SnippetContent { + + private List lines = new ArrayList<>(); + + protected void addLine(String line) { + this.lines.add(line); + } + + protected void addLine(int index, String line) { + this.lines.add(determineIndex(index), line); + } + + private int determineIndex(int index) { + if (index >= 0) { + return index; + } + return index + this.lines.size(); + } + + protected String getLinesAsString() { + StringWriter writer = new StringWriter(); + Iterator iterator = this.lines.iterator(); + while (iterator.hasNext()) { + writer.append(String.format("%s", iterator.next())); + if (iterator.hasNext()) { + writer.append(String.format("%n")); + } + } + return writer.toString(); + } + + } + +} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/CapturedOutput.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/CapturedOutput.java similarity index 77% rename from spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/CapturedOutput.java rename to spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/CapturedOutput.java index 699864f5..d3aaed82 100644 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/CapturedOutput.java +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/CapturedOutput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -14,16 +14,11 @@ * limitations under the License. */ -package org.springframework.restdocs.testfixtures; +package org.springframework.restdocs.testfixtures.jupiter; /** * Provides access to {@link System#out System.out} and {@link System#err System.err} - * output that has been captured by the {@link OutputCaptureRule}. Can be used to apply - * assertions using AssertJ. For example:
- * assertThat(output).contains("started"); // Checks all output
- * assertThat(output.getErr()).contains("failed"); // Only checks System.err
- * assertThat(output.getOut()).contains("ok"); // Only checks System.out
- * 
+ * output that has been captured. * * @author Madhura Bhave * @author Phillip Webb diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationBuilder.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OperationBuilder.java similarity index 93% rename from spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationBuilder.java rename to spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OperationBuilder.java index e3d3d40c..97c9ebcd 100644 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OperationBuilder.java +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OperationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.restdocs.testfixtures; +package org.springframework.restdocs.testfixtures.jupiter; import java.io.File; import java.net.URI; @@ -26,8 +26,6 @@ import java.util.Map; import java.util.Set; -import org.junit.runners.model.Statement; - import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -59,21 +57,27 @@ * * @author Andy Wilkinson */ -public class OperationBuilder extends OperationTestRule { +public class OperationBuilder { private final Map attributes = new HashMap<>(); - private OperationResponseBuilder responseBuilder; - - private String name; + private final File outputDirectory; - private File outputDirectory; + private final String name; private final TemplateFormat templateFormat; + private OperationResponseBuilder responseBuilder; + private OperationRequestBuilder requestBuilder; - public OperationBuilder(TemplateFormat templateFormat) { + OperationBuilder(File outputDirectory, String name) { + this(outputDirectory, name, null); + } + + OperationBuilder(File outputDirectory, String name, TemplateFormat templateFormat) { + this.outputDirectory = outputDirectory; + this.name = name; this.templateFormat = templateFormat; } @@ -92,13 +96,6 @@ public OperationBuilder attribute(String name, Object value) { return this; } - private void prepare(String operationName, File outputDirectory) { - this.name = operationName; - this.outputDirectory = outputDirectory; - this.requestBuilder = null; - this.attributes.clear(); - } - public Operation build() { if (this.attributes.get(TemplateEngine.class.getName()) == null) { Map templateContext = new HashMap<>(); @@ -127,12 +124,6 @@ private RestDocumentationContext createContext() { return context; } - @Override - public Statement apply(Statement base, File outputDirectory, String operationName) { - prepare(operationName, outputDirectory); - return base; - } - /** * Basic builder API for creating an {@link OperationRequest}. */ diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCapture.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCapture.java similarity index 94% rename from spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCapture.java rename to spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCapture.java index eef8d6a3..385dd49c 100644 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/OutputCapture.java +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCapture.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.restdocs.testfixtures; +package org.springframework.restdocs.testfixtures.jupiter; import java.io.IOException; import java.io.OutputStream; @@ -35,8 +35,6 @@ * @author Madhura Bhave * @author Phillip Webb * @author Andy Wilkinson - * @author Sam Brannen - * @see OutputCaptureRule */ class OutputCapture implements CapturedOutput { @@ -61,7 +59,7 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (obj instanceof CapturedOutput || obj instanceof CharSequence) { + if (obj instanceof CharSequence) { return getAll().equals(obj.toString()); } return false; @@ -123,17 +121,17 @@ private String get(Predicate filter) { } /** - * A capture session that captures {@link System#out System.out} and {@link System#out + * A capture session that captures {@link System#out System.out} and {@link System#err * System.err}. */ private static class SystemCapture { - private final Object monitor = new Object(); - private final PrintStreamCapture out; private final PrintStreamCapture err; + private final Object monitor = new Object(); + private final List capturedStrings = new ArrayList<>(); SystemCapture() { @@ -195,8 +193,8 @@ PrintStream getParent() { } private static PrintStream getSystemStream(PrintStream printStream) { - while (printStream instanceof PrintStreamCapture) { - printStream = ((PrintStreamCapture) printStream).getParent(); + while (printStream instanceof PrintStreamCapture printStreamCapture) { + printStream = printStreamCapture.getParent(); } return printStream; } diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java new file mode 100644 index 00000000..4dbb2b8e --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +/** + * JUnit Jupiter {@code @Extension} to capture {@link System#out System.out} and + * {@link System#err System.err}. Can be registered for an entire test class or for an + * individual test method through {@link ExtendWith @ExtendWith}. This extension provides + * {@linkplain ParameterResolver parameter resolution} for a {@link CapturedOutput} + * instance which can be used to assert that the correct output was written. + *

+ * To use with {@link ExtendWith @ExtendWith}, inject the {@link CapturedOutput} as an + * argument to your test class constructor, test method, or lifecycle methods: + * + *

+ * @ExtendWith(OutputCaptureExtension.class)
+ * class MyTest {
+ *
+ *     @Test
+ *     void test(CapturedOutput output) {
+ *         System.out.println("ok");
+ *         assertThat(output).contains("ok");
+ *         System.err.println("error");
+ *     }
+ *
+ *     @AfterEach
+ *     void after(CapturedOutput output) {
+ *         assertThat(output.getOut()).contains("ok");
+ *         assertThat(output.getErr()).contains("error");
+ *     }
+ *
+ * }
+ * 
+ * + * @author Madhura Bhave + * @author Phillip Webb + * @author Andy Wilkinson + * @author Sam Brannen + * @see CapturedOutput + */ +public class OutputCaptureExtension + implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { + + OutputCaptureExtension() { + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + getOutputCapture(context).push(); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + getOutputCapture(context).pop(); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + getOutputCapture(context).push(); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + getOutputCapture(context).pop(); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return CapturedOutput.class.equals(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return getOutputCapture(extensionContext); + } + + private OutputCapture getOutputCapture(ExtensionContext context) { + return getStore(context).getOrComputeIfAbsent(OutputCapture.class); + } + + private Store getStore(ExtensionContext context) { + return context.getStore(Namespace.create(getClass())); + } + +} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java new file mode 100644 index 00000000..15eb8d39 --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.restdocs.templates.TemplateFormats; + +/** + * Signals that a method is a template for a test that renders a snippet. The test will be + * executed once for each of the two supported snippet formats (Asciidoctor and Markdown). + *

+ * A rendered snippet test method can inject the following types: + *

    + *
  • {@link OperationBuilder}
  • + *
  • {@link AssertableSnippets}
  • + *
+ * + * @author Andy Wilkinson + */ +@TestTemplate +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(RenderedSnippetTestExtension.class) +public @interface RenderedSnippetTest { + + /** + * The snippet formats to render. + * @return the formats + */ + Format[] format() default { Format.ASCIIDOCTOR, Format.MARKDOWN }; + + enum Format { + + /** + * Asciidoctor snippet format. + */ + ASCIIDOCTOR(TemplateFormats.asciidoctor()), + + /** + * Markdown snippet format. + */ + MARKDOWN(TemplateFormats.markdown()); + + private final TemplateFormat templateFormat; + + Format(TemplateFormat templateFormat) { + this.templateFormat = templateFormat; + } + + TemplateFormat templateFormat() { + return this.templateFormat; + } + + } + +} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java new file mode 100644 index 00000000..34bb83ef --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java @@ -0,0 +1,155 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import java.io.File; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.junit.platform.commons.util.AnnotationUtils; + +import org.springframework.core.io.FileSystemResource; +import org.springframework.restdocs.templates.TemplateEngine; +import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.restdocs.templates.TemplateResourceResolver; +import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; +import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest.Format; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * {@link TestTemplateInvocationContextProvider} for + * {@link RenderedSnippetTest @RenderedSnippetTest} and + * {@link SnippetTemplate @SnippetTemplate}. + * + * @author Andy Wilkinson + */ +class RenderedSnippetTestExtension implements TestTemplateInvocationContextProvider { + + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } + + @Override + public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { + return AnnotationUtils.findAnnotation(context.getRequiredTestMethod(), RenderedSnippetTest.class) + .map((renderedSnippetTest) -> Stream.of(renderedSnippetTest.format()) + .map(Format::templateFormat) + .map(SnippetTestInvocationContext::new) + .map(TestTemplateInvocationContext.class::cast)) + .orElseThrow(); + } + + static class SnippetTestInvocationContext implements TestTemplateInvocationContext { + + private final TemplateFormat templateFormat; + + SnippetTestInvocationContext(TemplateFormat templateFormat) { + this.templateFormat = templateFormat; + } + + @Override + public List getAdditionalExtensions() { + return List.of(new RenderedSnippetTestParameterResolver(this.templateFormat)); + } + + @Override + public String getDisplayName(int invocationIndex) { + return this.templateFormat.getId(); + } + + } + + static class RenderedSnippetTestParameterResolver implements ParameterResolver { + + private final TemplateFormat templateFormat; + + RenderedSnippetTestParameterResolver(TemplateFormat templateFormat) { + this.templateFormat = templateFormat; + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + Class parameterType = parameterContext.getParameter().getType(); + return AssertableSnippets.class.equals(parameterType) || OperationBuilder.class.equals(parameterType) + || TemplateFormat.class.equals(parameterType); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + Class parameterType = parameterContext.getParameter().getType(); + if (AssertableSnippets.class.equals(parameterType)) { + return getStore(extensionContext).getOrComputeIfAbsent(AssertableSnippets.class, + (key) -> new AssertableSnippets(determineOutputDirectory(extensionContext), + determineOperationName(extensionContext), this.templateFormat)); + } + if (TemplateFormat.class.equals(parameterType)) { + return this.templateFormat; + } + return getStore(extensionContext).getOrComputeIfAbsent(OperationBuilder.class, (key) -> { + OperationBuilder operationBuilder = new OperationBuilder(determineOutputDirectory(extensionContext), + determineOperationName(extensionContext), this.templateFormat); + AnnotationUtils.findAnnotation(extensionContext.getRequiredTestMethod(), SnippetTemplate.class) + .ifPresent((snippetTemplate) -> { + TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); + given(resolver.resolveTemplateResource(snippetTemplate.snippet())) + .willReturn(snippetResource(snippetTemplate.template(), this.templateFormat)); + operationBuilder.attribute(TemplateEngine.class.getName(), + new MustacheTemplateEngine(resolver)); + }); + + return operationBuilder; + }); + } + + private Store getStore(ExtensionContext extensionContext) { + return extensionContext.getStore(Namespace.create(getClass())); + } + + private File determineOutputDirectory(ExtensionContext extensionContext) { + return new File("build/" + extensionContext.getRequiredTestClass().getSimpleName()); + } + + private String determineOperationName(ExtensionContext extensionContext) { + String operationName = extensionContext.getRequiredTestMethod().getName(); + int index = operationName.indexOf('['); + if (index > 0) { + operationName = operationName.substring(0, index); + } + return operationName; + } + + private FileSystemResource snippetResource(String name, TemplateFormat templateFormat) { + return new FileSystemResource( + "src/test/resources/custom-snippet-templates/" + templateFormat.getId() + "/" + name + ".snippet"); + } + + } + +} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java new file mode 100644 index 00000000..a07e34ce --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Customizes the template that will be used when rendering a snippet in a + * {@link RenderedSnippetTest rendered snippet test}. + * + * @author Andy Wilkinson + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SnippetTemplate { + + /** + * The name of the snippet whose template should be customized. + * @return the snippet name + */ + String snippet(); + + /** + * The custom template to use when rendering the snippet. + * @return the custom template + */ + String template(); + +} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java new file mode 100644 index 00000000..291f3e6f --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.restdocs.snippet.Snippet; + +/** + * Signals that a method is a test of a {@link Snippet}. Typically used to test scenarios + * where a failure occurs before the snippet is rendered. To test snippet rendering, use + * {@link RenderedSnippetTest}. + *

+ * A snippet test method can inject the following types: + *

    + *
  • {@link OperationBuilder}
  • + *
+ * + * @author Andy Wilkinson + */ +@Test +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(SnippetTestExtension.class) +public @interface SnippetTest { + +} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java new file mode 100644 index 00000000..1488902f --- /dev/null +++ b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.testfixtures.jupiter; + +import java.io.File; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +/** + * {@link ParameterResolver} for {@link SnippetTest @SnippetTest}. + * + * @author Andy Wilkinson + */ +class SnippetTestExtension implements ParameterResolver { + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + Class parameterType = parameterContext.getParameter().getType(); + return OperationBuilder.class.equals(parameterType); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return getStore(extensionContext).getOrComputeIfAbsent(OperationBuilder.class, + (key) -> new OperationBuilder(determineOutputDirectory(extensionContext), + determineOperationName(extensionContext))); + } + + private Store getStore(ExtensionContext extensionContext) { + return extensionContext.getStore(Namespace.create(getClass())); + } + + private File determineOutputDirectory(ExtensionContext extensionContext) { + return new File("build/" + extensionContext.getRequiredTestClass().getSimpleName()); + } + + private String determineOperationName(ExtensionContext extensionContext) { + String operationName = extensionContext.getRequiredTestMethod().getName(); + int index = operationName.indexOf('['); + if (index > 0) { + operationName = operationName.substring(0, index); + } + return operationName; + } + +} diff --git a/spring-restdocs-mockmvc/build.gradle b/spring-restdocs-mockmvc/build.gradle index d163b7dd..875d7414 100644 --- a/spring-restdocs-mockmvc/build.gradle +++ b/spring-restdocs-mockmvc/build.gradle @@ -16,8 +16,8 @@ dependencies { internal(platform(project(":spring-restdocs-platform"))) testImplementation(testFixtures(project(":spring-restdocs-core"))) - testImplementation("junit:junit") - testImplementation("org.assertj:assertj-core") - testImplementation("org.hamcrest:hamcrest-library") - testImplementation("org.mockito:mockito-core") +} + +tasks.named("test") { + useJUnitPlatform() } diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java index 5122510a..c0d3c1fc 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java @@ -23,7 +23,7 @@ import java.util.Iterator; import jakarta.servlet.http.Part; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -46,19 +46,19 @@ * * @author Andy Wilkinson */ -public class MockMvcRequestConverterTests { +class MockMvcRequestConverterTests { private final MockMvcRequestConverter factory = new MockMvcRequestConverter(); @Test - public void httpRequest() { + void httpRequest() { OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get("/foo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); } @Test - public void httpRequestWithCustomPort() { + void httpRequestWithCustomPort() { MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); mockRequest.setServerPort(8080); OperationRequest request = this.factory.convert(mockRequest); @@ -67,14 +67,14 @@ public void httpRequestWithCustomPort() { } @Test - public void requestWithContextPath() { + void requestWithContextPath() { OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get("/foo/bar").contextPath("/foo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo/bar")); assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); } @Test - public void requestWithHeaders() { + void requestWithHeaders() { OperationRequest request = createOperationRequest( MockMvcRequestBuilders.get("/foo").header("a", "alpha", "apple").header("b", "bravo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); @@ -84,7 +84,7 @@ public void requestWithHeaders() { } @Test - public void requestWithCookies() { + void requestWithCookies() { OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get("/foo") .cookie(new jakarta.servlet.http.Cookie("cookieName1", "cookieVal1"), new jakarta.servlet.http.Cookie("cookieName2", "cookieVal2"))); @@ -104,7 +104,7 @@ public void requestWithCookies() { } @Test - public void httpsRequest() { + void httpsRequest() { MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); mockRequest.setScheme("https"); mockRequest.setServerPort(443); @@ -114,7 +114,7 @@ public void httpsRequest() { } @Test - public void httpsRequestWithCustomPort() { + void httpsRequestWithCustomPort() { MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); mockRequest.setScheme("https"); mockRequest.setServerPort(8443); @@ -124,7 +124,7 @@ public void httpsRequestWithCustomPort() { } @Test - public void getRequestWithParametersProducesUriWithQueryString() { + void getRequestWithParametersProducesUriWithQueryString() { OperationRequest request = createOperationRequest( MockMvcRequestBuilders.get("/foo").param("a", "alpha", "apple").param("b", "br&vo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&a=apple&b=br%26vo")); @@ -132,7 +132,7 @@ public void getRequestWithParametersProducesUriWithQueryString() { } @Test - public void getRequestWithQueryString() { + void getRequestWithQueryString() { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/foo?a=alpha&b=bravo"); OperationRequest request = createOperationRequest(builder); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&b=bravo")); @@ -140,7 +140,7 @@ public void getRequestWithQueryString() { } @Test - public void postRequestWithParametersCreatesFormUrlEncodedContent() { + void postRequestWithParametersCreatesFormUrlEncodedContent() { OperationRequest request = createOperationRequest( MockMvcRequestBuilders.post("/foo").param("a", "alpha", "apple").param("b", "br&vo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); @@ -150,7 +150,7 @@ public void postRequestWithParametersCreatesFormUrlEncodedContent() { } @Test - public void postRequestWithParametersAndQueryStringCreatesFormUrlEncodedContentWithoutDuplication() { + void postRequestWithParametersAndQueryStringCreatesFormUrlEncodedContentWithoutDuplication() { OperationRequest request = createOperationRequest( MockMvcRequestBuilders.post("/foo?a=alpha").param("a", "apple").param("b", "br&vo")); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha")); @@ -160,7 +160,7 @@ public void postRequestWithParametersAndQueryStringCreatesFormUrlEncodedContentW } @Test - public void mockMultipartFileUpload() { + void mockMultipartFileUpload() { OperationRequest request = createOperationRequest(MockMvcRequestBuilders.multipart("/foo") .file(new MockMultipartFile("file", new byte[] { 1, 2, 3, 4 }))); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); @@ -175,7 +175,7 @@ public void mockMultipartFileUpload() { } @Test - public void mockMultipartFileUploadWithContentType() { + void mockMultipartFileUploadWithContentType() { OperationRequest request = createOperationRequest(MockMvcRequestBuilders.multipart("/foo") .file(new MockMultipartFile("file", "original", "image/png", new byte[] { 1, 2, 3, 4 }))); assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); @@ -189,7 +189,7 @@ public void mockMultipartFileUploadWithContentType() { } @Test - public void requestWithPart() throws IOException { + void requestWithPart() throws IOException { MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); Part mockPart = mock(Part.class); given(mockPart.getHeaderNames()).willReturn(Arrays.asList("a", "b")); @@ -211,7 +211,7 @@ public void requestWithPart() throws IOException { } @Test - public void requestWithPartWithContentType() throws IOException { + void requestWithPartWithContentType() throws IOException { MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); Part mockPart = mock(Part.class); given(mockPart.getHeaderNames()).willReturn(Arrays.asList("a", "b")); diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java index 300649a2..e13bb03b 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java @@ -20,7 +20,7 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -37,12 +37,12 @@ * * @author Tomasz Kopczynski */ -public class MockMvcResponseConverterTests { +class MockMvcResponseConverterTests { private final MockMvcResponseConverter factory = new MockMvcResponseConverter(); @Test - public void basicResponse() { + void basicResponse() { MockHttpServletResponse response = new MockHttpServletResponse(); response.setStatus(HttpServletResponse.SC_OK); OperationResponse operationResponse = this.factory.convert(response); @@ -50,7 +50,7 @@ public void basicResponse() { } @Test - public void responseWithCookie() { + void responseWithCookie() { MockHttpServletResponse response = new MockHttpServletResponse(); response.setStatus(HttpServletResponse.SC_OK); Cookie cookie = new Cookie("name", "value"); @@ -66,7 +66,7 @@ public void responseWithCookie() { } @Test - public void responseWithCustomStatus() { + void responseWithCustomStatus() { MockHttpServletResponse response = new MockHttpServletResponse(); response.setStatus(600); OperationResponse operationResponse = this.factory.convert(response); diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java index a1e3e9a0..dfb2debc 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,12 +19,13 @@ import java.lang.reflect.Method; import java.util.Map; -import org.junit.Assume; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.generate.RestDocumentationGenerator; import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.util.ReflectionUtils; @@ -41,24 +42,22 @@ * @author Andy Wilkinson * @author Dmitriy Mayboroda */ -public class MockMvcRestDocumentationConfigurerTests { +@ExtendWith(RestDocumentationExtension.class) +class MockMvcRestDocumentationConfigurerTests { private MockHttpServletRequest request = new MockHttpServletRequest(); - @Rule - public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - @Test - public void defaultConfiguration() { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation) + void defaultConfiguration(RestDocumentationContextProvider restDocumentation) { + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation) .beforeMockMvcCreated(null, null); postProcessor.postProcessRequest(this.request); assertUriConfiguration("http", "localhost", 8080); } @Test - public void customScheme() { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation).uris() + void customScheme(RestDocumentationContextProvider restDocumentation) { + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() .withScheme("https") .beforeMockMvcCreated(null, null); postProcessor.postProcessRequest(this.request); @@ -66,8 +65,8 @@ public void customScheme() { } @Test - public void customHost() { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation).uris() + void customHost(RestDocumentationContextProvider restDocumentation) { + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() .withHost("api.example.com") .beforeMockMvcCreated(null, null); postProcessor.postProcessRequest(this.request); @@ -75,8 +74,8 @@ public void customHost() { } @Test - public void customPort() { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation).uris() + void customPort(RestDocumentationContextProvider restDocumentation) { + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() .withPort(8081) .beforeMockMvcCreated(null, null); postProcessor.postProcessRequest(this.request); @@ -84,8 +83,8 @@ public void customPort() { } @Test - public void noContentLengthHeaderWhenRequestHasNotContent() { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation).uris() + void noContentLengthHeaderWhenRequestHasNotContent(RestDocumentationContextProvider restDocumentation) { + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() .withPort(8081) .beforeMockMvcCreated(null, null); postProcessor.postProcessRequest(this.request); @@ -94,8 +93,8 @@ public void noContentLengthHeaderWhenRequestHasNotContent() { @Test @SuppressWarnings("unchecked") - public void uriTemplateFromRequestAttribute() { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation) + void uriTemplateFromRequestAttribute(RestDocumentationContextProvider restDocumentation) { + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation) .beforeMockMvcCreated(null, null); this.request.setAttribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{a}/{b}"); postProcessor.postProcessRequest(this.request); @@ -106,11 +105,11 @@ public void uriTemplateFromRequestAttribute() { @Test @SuppressWarnings("unchecked") - public void uriTemplateFromRequest() { + void uriTemplateFromRequest(RestDocumentationContextProvider restDocumentation) { Method setUriTemplate = ReflectionUtils.findMethod(MockHttpServletRequest.class, "setUriTemplate", String.class); - Assume.assumeNotNull(setUriTemplate); - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(this.restDocumentation) + Assumptions.assumeFalse(setUriTemplate == null); + RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation) .beforeMockMvcCreated(null, null); ReflectionUtils.invokeMethod(setUriTemplate, this.request, "{a}/{b}"); postProcessor.postProcessRequest(this.request); diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java index 2e35c950..49460f1e 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 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. @@ -34,11 +34,10 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import org.assertj.core.api.Condition; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -47,7 +46,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationIntegrationTests.TestConfiguration; import org.springframework.restdocs.templates.TemplateFormat; import org.springframework.restdocs.templates.TemplateFormats; @@ -56,7 +56,7 @@ import org.springframework.restdocs.testfixtures.SnippetConditions.HttpRequestCondition; import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -106,29 +106,30 @@ * @author Tomasz Kopczynski * @author Filip Hrisafov */ -@RunWith(SpringJUnit4ClassRunner.class) +@SpringJUnitConfig @WebAppConfiguration +@ExtendWith(RestDocumentationExtension.class) @ContextConfiguration(classes = TestConfiguration.class) public class MockMvcRestDocumentationIntegrationTests { - @Rule - public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + private RestDocumentationContextProvider restDocumentation; @Autowired private WebApplicationContext context; - @Before - public void deleteSnippets() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { + this.restDocumentation = restDocumentation; FileSystemUtils.deleteRecursively(new File("build/generated-snippets")); } - @After - public void clearOutputDirSystemProperty() { + @AfterEach + void clearOutputDirSystemProperty() { System.clearProperty("org.springframework.restdocs.outputDir"); } @Test - public void basicSnippetGeneration() throws Exception { + void basicSnippetGeneration() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation).snippets().withEncoding("UTF-8")) .build(); @@ -140,7 +141,19 @@ public void basicSnippetGeneration() throws Exception { } @Test - public void markdownSnippetGeneration() throws Exception { + void getRequestWithBody() throws Exception { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation).snippets().withEncoding("UTF-8")) + .build(); + mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON).content("some body content")) + .andExpect(status().isOk()) + .andDo(document("get-request-with-body")); + assertExpectedSnippetFilesExist(new File("build/generated-snippets/get-request-with-body"), "http-request.adoc", + "http-response.adoc", "curl-request.adoc"); + } + + @Test + void markdownSnippetGeneration() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation).snippets() .withEncoding("UTF-8") @@ -154,7 +167,7 @@ public void markdownSnippetGeneration() throws Exception { } @Test - public void curlSnippetWithContent() throws Exception { + void curlSnippetWithContent() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -168,7 +181,7 @@ public void curlSnippetWithContent() throws Exception { } @Test - public void curlSnippetWithCookies() throws Exception { + void curlSnippetWithCookies() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -182,7 +195,7 @@ public void curlSnippetWithCookies() throws Exception { } @Test - public void curlSnippetWithQueryStringOnPost() throws Exception { + void curlSnippetWithQueryStringOnPost() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -196,7 +209,7 @@ public void curlSnippetWithQueryStringOnPost() throws Exception { } @Test - public void curlSnippetWithEmptyParameterQueryString() throws Exception { + void curlSnippetWithEmptyParameterQueryString() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -210,7 +223,7 @@ public void curlSnippetWithEmptyParameterQueryString() throws Exception { } @Test - public void curlSnippetWithContentAndParametersOnPost() throws Exception { + void curlSnippetWithContentAndParametersOnPost() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -224,7 +237,7 @@ public void curlSnippetWithContentAndParametersOnPost() throws Exception { } @Test - public void httpieSnippetWithContent() throws Exception { + void httpieSnippetWithContent() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -237,7 +250,7 @@ public void httpieSnippetWithContent() throws Exception { } @Test - public void httpieSnippetWithCookies() throws Exception { + void httpieSnippetWithCookies() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -251,7 +264,7 @@ public void httpieSnippetWithCookies() throws Exception { } @Test - public void httpieSnippetWithQueryStringOnPost() throws Exception { + void httpieSnippetWithQueryStringOnPost() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -265,7 +278,7 @@ public void httpieSnippetWithQueryStringOnPost() throws Exception { } @Test - public void httpieSnippetWithContentAndParametersOnPost() throws Exception { + void httpieSnippetWithContentAndParametersOnPost() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -280,7 +293,7 @@ public void httpieSnippetWithContentAndParametersOnPost() throws Exception { } @Test - public void linksSnippet() throws Exception { + void linksSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -293,7 +306,7 @@ public void linksSnippet() throws Exception { } @Test - public void pathParametersSnippet() throws Exception { + void pathParametersSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -305,7 +318,7 @@ public void pathParametersSnippet() throws Exception { } @Test - public void queryParametersSnippet() throws Exception { + void queryParametersSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -317,7 +330,7 @@ public void queryParametersSnippet() throws Exception { } @Test - public void requestFieldsSnippet() throws Exception { + void requestFieldsSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -329,7 +342,7 @@ public void requestFieldsSnippet() throws Exception { } @Test - public void requestPartsSnippet() throws Exception { + void requestPartsSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -341,7 +354,7 @@ public void requestPartsSnippet() throws Exception { } @Test - public void responseFieldsSnippet() throws Exception { + void responseFieldsSnippet() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -354,7 +367,7 @@ public void responseFieldsSnippet() throws Exception { } @Test - public void responseWithSetCookie() throws Exception { + void responseWithSetCookie() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -368,7 +381,7 @@ public void responseWithSetCookie() throws Exception { } @Test - public void parameterizedOutputDirectory() throws Exception { + void parameterizedOutputDirectory() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -380,7 +393,7 @@ public void parameterizedOutputDirectory() throws Exception { } @Test - public void multiStep() throws Exception { + void multiStep() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .alwaysDo(document("{method-name}-{step}")) @@ -398,7 +411,7 @@ public void multiStep() throws Exception { } @Test - public void alwaysDoWithAdditionalSnippets() throws Exception { + void alwaysDoWithAdditionalSnippets() throws Exception { RestDocumentationResultHandler documentation = document("{method-name}-{step}"); MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) @@ -412,7 +425,7 @@ public void alwaysDoWithAdditionalSnippets() throws Exception { } @Test - public void preprocessedRequest() throws Exception { + void preprocessedRequest() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -455,7 +468,7 @@ public void preprocessedRequest() throws Exception { } @Test - public void defaultPreprocessedRequest() throws Exception { + void defaultPreprocessedRequest() throws Exception { Pattern pattern = Pattern.compile("(\"alpha\")"); MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation).operationPreprocessors() @@ -486,7 +499,7 @@ public void defaultPreprocessedRequest() throws Exception { } @Test - public void preprocessedResponse() throws Exception { + void preprocessedResponse() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -515,7 +528,7 @@ public void preprocessedResponse() throws Exception { } @Test - public void defaultPreprocessedResponse() throws Exception { + void defaultPreprocessedResponse() throws Exception { Pattern pattern = Pattern.compile("(\"alpha\")"); MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation).operationPreprocessors() @@ -537,7 +550,7 @@ public void defaultPreprocessedResponse() throws Exception { } @Test - public void customSnippetTemplate() throws Exception { + void customSnippetTemplate() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -559,7 +572,7 @@ public void customSnippetTemplate() throws Exception { } @Test - public void customContextPath() throws Exception { + void customContextPath() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); @@ -573,7 +586,7 @@ public void customContextPath() throws Exception { } @Test - public void exceptionShouldBeThrownWhenCallDocumentMockMvcNotConfigured() { + void exceptionShouldBeThrownWhenCallDocumentMockMvcNotConfigured() { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); assertThatThrownBy(() -> mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andDo(document("basic"))) .isInstanceOf(IllegalStateException.class) @@ -583,7 +596,7 @@ public void exceptionShouldBeThrownWhenCallDocumentMockMvcNotConfigured() { } @Test - public void exceptionShouldBeThrownWhenCallDocumentSnippetsMockMvcNotConfigured() { + void exceptionShouldBeThrownWhenCallDocumentSnippetsMockMvcNotConfigured() { RestDocumentationResultHandler documentation = document("{method-name}-{step}"); MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); assertThatThrownBy(() -> mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) @@ -594,7 +607,7 @@ public void exceptionShouldBeThrownWhenCallDocumentSnippetsMockMvcNotConfigured( } @Test - public void multiPart() throws Exception { + void multiPart() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java index b6a25d46..f689778a 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import java.net.URI; import jakarta.servlet.ServletContext; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpServletRequest; @@ -44,97 +44,97 @@ * @author Andy Wilkinson * */ -public class RestDocumentationRequestBuildersTests { +class RestDocumentationRequestBuildersTests { private final ServletContext servletContext = new MockServletContext(); @Test - public void getTemplate() { + void getTemplate() { assertTemplate(get("/{template}", "t"), HttpMethod.GET); } @Test - public void getUri() { + void getUri() { assertUri(get(URI.create("/uri")), HttpMethod.GET); } @Test - public void postTemplate() { + void postTemplate() { assertTemplate(post("/{template}", "t"), HttpMethod.POST); } @Test - public void postUri() { + void postUri() { assertUri(post(URI.create("/uri")), HttpMethod.POST); } @Test - public void putTemplate() { + void putTemplate() { assertTemplate(put("/{template}", "t"), HttpMethod.PUT); } @Test - public void putUri() { + void putUri() { assertUri(put(URI.create("/uri")), HttpMethod.PUT); } @Test - public void patchTemplate() { + void patchTemplate() { assertTemplate(patch("/{template}", "t"), HttpMethod.PATCH); } @Test - public void patchUri() { + void patchUri() { assertUri(patch(URI.create("/uri")), HttpMethod.PATCH); } @Test - public void deleteTemplate() { + void deleteTemplate() { assertTemplate(delete("/{template}", "t"), HttpMethod.DELETE); } @Test - public void deleteUri() { + void deleteUri() { assertUri(delete(URI.create("/uri")), HttpMethod.DELETE); } @Test - public void optionsTemplate() { + void optionsTemplate() { assertTemplate(options("/{template}", "t"), HttpMethod.OPTIONS); } @Test - public void optionsUri() { + void optionsUri() { assertUri(options(URI.create("/uri")), HttpMethod.OPTIONS); } @Test - public void headTemplate() { + void headTemplate() { assertTemplate(head("/{template}", "t"), HttpMethod.HEAD); } @Test - public void headUri() { + void headUri() { assertUri(head(URI.create("/uri")), HttpMethod.HEAD); } @Test - public void requestTemplate() { + void requestTemplate() { assertTemplate(request(HttpMethod.GET, "/{template}", "t"), HttpMethod.GET); } @Test - public void requestUri() { + void requestUri() { assertUri(request(HttpMethod.GET, URI.create("/uri")), HttpMethod.GET); } @Test - public void multipartTemplate() { + void multipartTemplate() { assertTemplate(multipart("/{template}", "t"), HttpMethod.POST); } @Test - public void multipartUri() { + void multipartUri() { assertUri(multipart(URI.create("/uri")), HttpMethod.POST); } diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle index 28f64f14..a4928f8e 100644 --- a/spring-restdocs-platform/build.gradle +++ b/spring-restdocs-platform/build.gradle @@ -15,6 +15,7 @@ dependencies { api("org.apache.pdfbox:pdfbox:2.0.27") api("org.apache.tomcat.embed:tomcat-embed-core:11.0.2") api("org.apache.tomcat.embed:tomcat-embed-el:11.0.2") + api("org.apiguardian:apiguardian-api:1.1.2") api("org.asciidoctor:asciidoctorj:3.0.0") api("org.asciidoctor:asciidoctorj-pdf:2.3.19") api("org.assertj:assertj-core:3.23.1") @@ -22,10 +23,10 @@ dependencies { api("org.hamcrest:hamcrest-library:1.3") api("org.hibernate.validator:hibernate-validator:9.0.0.CR1") api("org.javamoney:moneta:1.4.2") - api("org.junit.jupiter:junit-jupiter-api:5.0.0") } api(enforcedPlatform("com.fasterxml.jackson:jackson-bom:2.14.0")) api(enforcedPlatform("io.rest-assured:rest-assured-bom:5.2.1")) api(enforcedPlatform("org.mockito:mockito-bom:4.9.0")) + api(enforcedPlatform("org.junit:junit-bom:5.13.0")) api(enforcedPlatform("org.springframework:spring-framework-bom:$springFrameworkVersion")) } diff --git a/spring-restdocs-restassured/build.gradle b/spring-restdocs-restassured/build.gradle index a3216017..42a05a52 100644 --- a/spring-restdocs-restassured/build.gradle +++ b/spring-restdocs-restassured/build.gradle @@ -13,13 +13,14 @@ dependencies { internal(platform(project(":spring-restdocs-platform"))) + testCompileOnly("org.apiguardian:apiguardian-api") testImplementation(testFixtures(project(":spring-restdocs-core"))) testImplementation("com.fasterxml.jackson.core:jackson-databind") - testImplementation("junit:junit") testImplementation("org.apache.tomcat.embed:tomcat-embed-core") - testImplementation("org.assertj:assertj-core") - testImplementation("org.hamcrest:hamcrest-library") - testImplementation("org.mockito:mockito-core") +} + +tasks.named("test") { + useJUnitPlatform(); } compatibilityTest { diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java index 4d7438a5..1bef2929 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,8 +19,8 @@ import io.restassured.RestAssured; import io.restassured.specification.RequestSpecification; import org.assertj.core.api.AbstractAssert; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -34,12 +34,12 @@ * * @author Andy Wilkinson */ -public class RestAssuredParameterBehaviorTests { +class RestAssuredParameterBehaviorTests { private static final MediaType APPLICATION_FORM_URLENCODED_ISO_8859_1 = MediaType .parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=ISO-8859-1"); - @ClassRule + @RegisterExtension public static TomcatServer tomcat = new TomcatServer(); private final RestAssuredRequestConverter factory = new RestAssuredRequestConverter(); @@ -54,7 +54,7 @@ public class RestAssuredParameterBehaviorTests { }); @Test - public void queryParameterOnGet() { + void queryParameterOnGet() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .get("/query-parameter") @@ -64,7 +64,7 @@ public void queryParameterOnGet() { } @Test - public void queryParameterOnHead() { + void queryParameterOnHead() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .head("/query-parameter") @@ -74,7 +74,7 @@ public void queryParameterOnHead() { } @Test - public void queryParameterOnPost() { + void queryParameterOnPost() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .post("/query-parameter") @@ -84,7 +84,7 @@ public void queryParameterOnPost() { } @Test - public void queryParameterOnPut() { + void queryParameterOnPut() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .put("/query-parameter") @@ -94,7 +94,7 @@ public void queryParameterOnPut() { } @Test - public void queryParameterOnPatch() { + void queryParameterOnPatch() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .patch("/query-parameter") @@ -104,7 +104,7 @@ public void queryParameterOnPatch() { } @Test - public void queryParameterOnDelete() { + void queryParameterOnDelete() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .delete("/query-parameter") @@ -114,7 +114,7 @@ public void queryParameterOnDelete() { } @Test - public void queryParameterOnOptions() { + void queryParameterOnOptions() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .options("/query-parameter") @@ -124,49 +124,49 @@ public void queryParameterOnOptions() { } @Test - public void paramOnGet() { + void paramOnGet() { this.spec.param("a", "alpha", "apple").param("b", "bravo").get("/query-parameter").then().statusCode(200); assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.GET); } @Test - public void paramOnHead() { + void paramOnHead() { this.spec.param("a", "alpha", "apple").param("b", "bravo").head("/query-parameter").then().statusCode(200); assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.HEAD); } @Test - public void paramOnPost() { + void paramOnPost() { this.spec.param("a", "alpha", "apple").param("b", "bravo").post("/form-url-encoded").then().statusCode(200); assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.POST); } @Test - public void paramOnPut() { + void paramOnPut() { this.spec.param("a", "alpha", "apple").param("b", "bravo").put("/query-parameter").then().statusCode(200); assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.PUT); } @Test - public void paramOnPatch() { + void paramOnPatch() { this.spec.param("a", "alpha", "apple").param("b", "bravo").patch("/query-parameter").then().statusCode(200); assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.PATCH); } @Test - public void paramOnDelete() { + void paramOnDelete() { this.spec.param("a", "alpha", "apple").param("b", "bravo").delete("/query-parameter").then().statusCode(200); assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.DELETE); } @Test - public void paramOnOptions() { + void paramOnOptions() { this.spec.param("a", "alpha", "apple").param("b", "bravo").options("/query-parameter").then().statusCode(200); assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.OPTIONS); } @Test - public void formParamOnGet() { + void formParamOnGet() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .get("/query-parameter") @@ -176,7 +176,7 @@ public void formParamOnGet() { } @Test - public void formParamOnHead() { + void formParamOnHead() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .head("/form-url-encoded") @@ -186,7 +186,7 @@ public void formParamOnHead() { } @Test - public void formParamOnPost() { + void formParamOnPost() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .post("/form-url-encoded") @@ -196,7 +196,7 @@ public void formParamOnPost() { } @Test - public void formParamOnPut() { + void formParamOnPut() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .put("/form-url-encoded") @@ -206,7 +206,7 @@ public void formParamOnPut() { } @Test - public void formParamOnPatch() { + void formParamOnPatch() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .patch("/form-url-encoded") @@ -216,7 +216,7 @@ public void formParamOnPatch() { } @Test - public void formParamOnDelete() { + void formParamOnDelete() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .delete("/form-url-encoded") @@ -226,7 +226,7 @@ public void formParamOnDelete() { } @Test - public void formParamOnOptions() { + void formParamOnOptions() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .options("/form-url-encoded") diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java index e7abf05d..09d411ae 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java @@ -28,8 +28,8 @@ import io.restassured.RestAssured; import io.restassured.specification.FilterableRequestSpecification; import io.restassured.specification.RequestSpecification; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -47,15 +47,15 @@ * * @author Andy Wilkinson */ -public class RestAssuredRequestConverterTests { +class RestAssuredRequestConverterTests { - @ClassRule + @RegisterExtension public static TomcatServer tomcat = new TomcatServer(); private final RestAssuredRequestConverter factory = new RestAssuredRequestConverter(); @Test - public void requestUri() { + void requestUri() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()); requestSpec.get("/foo/bar"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -63,7 +63,7 @@ public void requestUri() { } @Test - public void requestMethod() { + void requestMethod() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()); requestSpec.head("/foo/bar"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -71,7 +71,7 @@ public void requestMethod() { } @Test - public void queryStringParameters() { + void queryStringParameters() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).queryParam("foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -79,7 +79,7 @@ public void queryStringParameters() { } @Test - public void queryStringFromUrlParameters() { + void queryStringFromUrlParameters() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()); requestSpec.get("/?foo=bar&foo=qix"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -87,7 +87,7 @@ public void queryStringFromUrlParameters() { } @Test - public void paramOnGetRequestIsMappedToQueryString() { + void paramOnGetRequestIsMappedToQueryString() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).param("foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -95,7 +95,7 @@ public void paramOnGetRequestIsMappedToQueryString() { } @Test - public void headers() { + void headers() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).header("Foo", "bar"); requestSpec.get("/"); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -104,7 +104,7 @@ public void headers() { } @Test - public void headersWithCustomAccept() { + void headersWithCustomAccept() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .header("Foo", "bar") @@ -117,7 +117,7 @@ public void headersWithCustomAccept() { } @Test - public void cookies() { + void cookies() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .cookie("cookie1", "cookieVal1") @@ -138,7 +138,7 @@ public void cookies() { } @Test - public void multipart() { + void multipart() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart("a", "a.txt", "alpha", null) @@ -156,14 +156,14 @@ public void multipart() { } @Test - public void byteArrayBody() { + void byteArrayBody() { RequestSpecification requestSpec = RestAssured.given().body("body".getBytes()).port(tomcat.getPort()); requestSpec.post(); this.factory.convert((FilterableRequestSpecification) requestSpec); } @Test - public void stringBody() { + void stringBody() { RequestSpecification requestSpec = RestAssured.given().body("body").port(tomcat.getPort()); requestSpec.post(); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -171,7 +171,7 @@ public void stringBody() { } @Test - public void objectBody() { + void objectBody() { RequestSpecification requestSpec = RestAssured.given().body(new ObjectBody("bar")).port(tomcat.getPort()); requestSpec.post(); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -179,7 +179,7 @@ public void objectBody() { } @Test - public void byteArrayInputStreamBody() { + void byteArrayInputStreamBody() { RequestSpecification requestSpec = RestAssured.given() .body(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 })) .port(tomcat.getPort()); @@ -189,7 +189,7 @@ public void byteArrayInputStreamBody() { } @Test - public void fileBody() { + void fileBody() { RequestSpecification requestSpec = RestAssured.given() .body(new File("src/test/resources/body.txt")) .port(tomcat.getPort()); @@ -199,7 +199,7 @@ public void fileBody() { } @Test - public void fileInputStreamBody() throws FileNotFoundException { + void fileInputStreamBody() throws FileNotFoundException { FileInputStream inputStream = new FileInputStream("src/test/resources/body.txt"); RequestSpecification requestSpec = RestAssured.given().body(inputStream).port(tomcat.getPort()); requestSpec.post(); @@ -209,7 +209,7 @@ public void fileInputStreamBody() throws FileNotFoundException { } @Test - public void multipartWithByteArrayInputStreamBody() { + void multipartWithByteArrayInputStreamBody() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart("foo", "foo.txt", new ByteArrayInputStream("foo".getBytes())); @@ -219,7 +219,7 @@ public void multipartWithByteArrayInputStreamBody() { } @Test - public void multipartWithStringBody() { + void multipartWithStringBody() { RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("control", "foo"); requestSpec.post(); OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); @@ -227,7 +227,7 @@ public void multipartWithStringBody() { } @Test - public void multipartWithByteArrayBody() { + void multipartWithByteArrayBody() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart("control", "file", "foo".getBytes()); @@ -237,7 +237,7 @@ public void multipartWithByteArrayBody() { } @Test - public void multipartWithFileBody() { + void multipartWithFileBody() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart(new File("src/test/resources/body.txt")); @@ -247,7 +247,7 @@ public void multipartWithFileBody() { } @Test - public void multipartWithFileInputStreamBody() throws FileNotFoundException { + void multipartWithFileInputStreamBody() throws FileNotFoundException { FileInputStream inputStream = new FileInputStream("src/test/resources/body.txt"); RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) @@ -259,7 +259,7 @@ public void multipartWithFileInputStreamBody() throws FileNotFoundException { } @Test - public void multipartWithObjectBody() { + void multipartWithObjectBody() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart("control", new ObjectBody("bar")); diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java index 3760c656..22b17601 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -19,7 +19,7 @@ import io.restassured.http.Headers; import io.restassured.response.Response; import io.restassured.response.ResponseBody; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatusCode; import org.springframework.restdocs.operation.OperationResponse; @@ -33,12 +33,12 @@ * * @author Andy Wilkinson */ -public class RestAssuredResponseConverterTests { +class RestAssuredResponseConverterTests { private final RestAssuredResponseConverter converter = new RestAssuredResponseConverter(); @Test - public void responseWithCustomStatus() { + void responseWithCustomStatus() { Response response = mock(Response.class); given(response.getStatusCode()).willReturn(600); given(response.getHeaders()).willReturn(new Headers()); diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java index af44ae4c..8dbf5935 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -22,11 +22,13 @@ import io.restassured.filter.FilterContext; import io.restassured.specification.FilterableRequestSpecification; import io.restassured.specification.FilterableResponseSpecification; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.generate.RestDocumentationGenerator; import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; @@ -45,10 +47,8 @@ * @author Andy Wilkinson * @author Filip Hrisafov */ -public class RestAssuredRestDocumentationConfigurerTests { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class RestAssuredRestDocumentationConfigurerTests { private final FilterableRequestSpecification requestSpec = mock(FilterableRequestSpecification.class); @@ -56,17 +56,21 @@ public class RestAssuredRestDocumentationConfigurerTests { private final FilterContext filterContext = mock(FilterContext.class); - private final RestAssuredRestDocumentationConfigurer configurer = new RestAssuredRestDocumentationConfigurer( - this.restDocumentation); + private RestAssuredRestDocumentationConfigurer configurer; + + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { + this.configurer = new RestAssuredRestDocumentationConfigurer(restDocumentation); + } @Test - public void nextFilterIsCalled() { + void nextFilterIsCalled() { this.configurer.filter(this.requestSpec, this.responseSpec, this.filterContext); verify(this.filterContext).next(this.requestSpec, this.responseSpec); } @Test - public void configurationIsAddedToTheContext() { + void configurationIsAddedToTheContext() { this.configurer.operationPreprocessors() .withRequestDefaults(Preprocessors.prettyPrint()) .withResponseDefaults(Preprocessors.modifyHeaders().remove("Foo")) diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java index 740f886c..89d9c351 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -29,14 +29,15 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; import org.assertj.core.api.Condition; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.templates.TemplateFormat; import org.springframework.restdocs.templates.TemplateFormats; import org.springframework.restdocs.testfixtures.SnippetConditions; @@ -80,18 +81,16 @@ * @author Tomasz Kopczynski * @author Filip Hrisafov */ -public class RestAssuredRestDocumentationIntegrationTests { +@ExtendWith(RestDocumentationExtension.class) +class RestAssuredRestDocumentationIntegrationTests { - @Rule - public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - - @ClassRule - public static TomcatServer tomcat = new TomcatServer(); + @RegisterExtension + private static TomcatServer tomcat = new TomcatServer(); @Test - public void defaultSnippetGeneration() { + void defaultSnippetGeneration(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("default")) .get("/") .then() @@ -101,10 +100,10 @@ public void defaultSnippetGeneration() { } @Test - public void curlSnippetWithContent() { + void curlSnippetWithContent(RestDocumentationContextProvider restDocumentation) { String contentType = "text/plain; charset=UTF-8"; given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("curl-snippet-with-content")) .accept("application/json") .body("content") @@ -120,10 +119,10 @@ public void curlSnippetWithContent() { } @Test - public void curlSnippetWithCookies() { + void curlSnippetWithCookies(RestDocumentationContextProvider restDocumentation) { String contentType = "text/plain; charset=UTF-8"; given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("curl-snippet-with-cookies")) .accept("application/json") .contentType(contentType) @@ -138,9 +137,9 @@ public void curlSnippetWithCookies() { } @Test - public void curlSnippetWithEmptyParameterQueryString() { + void curlSnippetWithEmptyParameterQueryString(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("curl-snippet-with-empty-parameter-query-string")) .accept("application/json") .param("a", "") @@ -155,9 +154,9 @@ public void curlSnippetWithEmptyParameterQueryString() { } @Test - public void curlSnippetWithQueryStringOnPost() { + void curlSnippetWithQueryStringOnPost(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("curl-snippet-with-query-string")) .accept("application/json") .param("foo", "bar") @@ -174,9 +173,9 @@ public void curlSnippetWithQueryStringOnPost() { } @Test - public void linksSnippet() { + void linksSnippet(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("links", links(linkWithRel("rel").description("The description")))) .accept("application/json") .get("/") @@ -187,9 +186,9 @@ public void linksSnippet() { } @Test - public void pathParametersSnippet() { + void pathParametersSnippet(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("path-parameters", pathParameters(parameterWithName("foo").description("The description")))) .accept("application/json") @@ -201,9 +200,9 @@ public void pathParametersSnippet() { } @Test - public void queryParametersSnippet() { + void queryParametersSnippet(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("query-parameters", queryParameters(parameterWithName("foo").description("The description")))) .accept("application/json") @@ -216,9 +215,9 @@ public void queryParametersSnippet() { } @Test - public void requestFieldsSnippet() { + void requestFieldsSnippet(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("request-fields", requestFields(fieldWithPath("a").description("The description")))) .accept("application/json") .body("{\"a\":\"alpha\"}") @@ -230,9 +229,9 @@ public void requestFieldsSnippet() { } @Test - public void requestPartsSnippet() { + void requestPartsSnippet(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("request-parts", requestParts(partWithName("a").description("The description")))) .multiPart("a", "foo") .post("/upload") @@ -243,9 +242,9 @@ public void requestPartsSnippet() { } @Test - public void responseFieldsSnippet() { + void responseFieldsSnippet(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("response-fields", responseFields(fieldWithPath("a").description("The description"), subsectionWithPath("links").description("Links to other resources")))) @@ -258,9 +257,9 @@ public void responseFieldsSnippet() { } @Test - public void parameterizedOutputDirectory() { + void parameterizedOutputDirectory(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("{method-name}")) .get("/") .then() @@ -270,9 +269,9 @@ public void parameterizedOutputDirectory() { } @Test - public void multiStep() { + void multiStep(RestDocumentationContextProvider restDocumentation) { RequestSpecification spec = new RequestSpecBuilder().setPort(tomcat.getPort()) - .addFilter(documentationConfiguration(this.restDocumentation)) + .addFilter(documentationConfiguration(restDocumentation)) .addFilter(document("{method-name}-{step}")) .build(); given(spec).get("/").then().statusCode(200); @@ -287,10 +286,10 @@ public void multiStep() { } @Test - public void additionalSnippets() { + void additionalSnippets(RestDocumentationContextProvider restDocumentation) { RestDocumentationFilter documentation = document("{method-name}-{step}"); RequestSpecification spec = new RequestSpecBuilder().setPort(tomcat.getPort()) - .addFilter(documentationConfiguration(this.restDocumentation)) + .addFilter(documentationConfiguration(restDocumentation)) .addFilter(documentation) .build(); given(spec) @@ -304,9 +303,9 @@ public void additionalSnippets() { } @Test - public void responseWithCookie() { + void responseWithCookie(RestDocumentationContextProvider restDocumentation) { given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("set-cookie", preprocessResponse(modifyHeaders().remove(HttpHeaders.DATE).remove(HttpHeaders.CONTENT_TYPE)))) .get("/set-cookie") @@ -322,10 +321,10 @@ public void responseWithCookie() { } @Test - public void preprocessedRequest() { + void preprocessedRequest(RestDocumentationContextProvider restDocumentation) { Pattern pattern = Pattern.compile("(\"alpha\")"); given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .header("a", "alpha") .header("b", "bravo") .contentType("application/json") @@ -356,10 +355,10 @@ public void preprocessedRequest() { } @Test - public void defaultPreprocessedRequest() { + void defaultPreprocessedRequest(RestDocumentationContextProvider restDocumentation) { Pattern pattern = Pattern.compile("(\"alpha\")"); given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation).operationPreprocessors() + .filter(documentationConfiguration(restDocumentation).operationPreprocessors() .withRequestDefaults(prettyPrint(), replacePattern(pattern, "\"<>\""), modifyUris().removePort(), modifyHeaders().remove("a").remove(HttpHeaders.CONTENT_LENGTH))) .header("a", "alpha") @@ -381,10 +380,10 @@ public void defaultPreprocessedRequest() { } @Test - public void preprocessedResponse() { + void preprocessedResponse(RestDocumentationContextProvider restDocumentation) { Pattern pattern = Pattern.compile("(\"alpha\")"); given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("original-response")) .filter(document("preprocessed-response", preprocessResponse(prettyPrint(), maskLinks(), @@ -407,10 +406,10 @@ public void preprocessedResponse() { } @Test - public void defaultPreprocessedResponse() { + void defaultPreprocessedResponse(RestDocumentationContextProvider restDocumentation) { Pattern pattern = Pattern.compile("(\"alpha\")"); given().port(tomcat.getPort()) - .filter(documentationConfiguration(this.restDocumentation).operationPreprocessors() + .filter(documentationConfiguration(restDocumentation).operationPreprocessors() .withResponseDefaults(prettyPrint(), maskLinks(), modifyHeaders().remove("a").remove("Transfer-Encoding").remove("Date").remove("Server"), replacePattern(pattern, "\"<>\""), @@ -432,7 +431,7 @@ public void defaultPreprocessedResponse() { } @Test - public void customSnippetTemplate() throws MalformedURLException { + void customSnippetTemplate(RestDocumentationContextProvider restDocumentation) throws MalformedURLException { ClassLoader classLoader = new URLClassLoader( new URL[] { new File("src/test/resources/custom-snippet-templates").toURI().toURL() }, getClass().getClassLoader()); @@ -441,7 +440,7 @@ public void customSnippetTemplate() throws MalformedURLException { try { given().port(tomcat.getPort()) .accept("application/json") - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .filter(document("custom-snippet-template")) .get("/") .then() @@ -455,7 +454,7 @@ public void customSnippetTemplate() throws MalformedURLException { } @Test - public void exceptionShouldBeThrownWhenCallDocumentRequestSpecificationNotConfigured() { + void exceptionShouldBeThrownWhenCallDocumentRequestSpecificationNotConfigured() { assertThatThrownBy(() -> given().port(tomcat.getPort()).filter(document("default")).get("/")) .isInstanceOf(IllegalStateException.class) .hasMessage("REST Docs configuration not found. Did you forget to add a " @@ -463,7 +462,7 @@ public void exceptionShouldBeThrownWhenCallDocumentRequestSpecificationNotConfig } @Test - public void exceptionShouldBeThrownWhenCallDocumentSnippetsRequestSpecificationNotConfigured() { + void exceptionShouldBeThrownWhenCallDocumentSnippetsRequestSpecificationNotConfigured() { RestDocumentationFilter documentation = document("{method-name}-{step}"); assertThatThrownBy(() -> given().port(tomcat.getPort()) .filter(documentation.document(responseHeaders(headerWithName("a").description("one")))) diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java index e2da4c8f..f91e1943 100644 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java +++ b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -32,46 +32,57 @@ import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; import org.apache.catalina.startup.Tomcat; -import org.junit.rules.ExternalResource; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.springframework.http.MediaType; import org.springframework.util.FileCopyUtils; /** - * {@link ExternalResource} that starts and stops a Tomcat server. + * {@link Extension} that starts and stops a Tomcat server. * * @author Andy Wilkinson */ -class TomcatServer extends ExternalResource { - - private Tomcat tomcat; +class TomcatServer implements BeforeAllCallback, AfterAllCallback { private int port; @Override - protected void before() throws LifecycleException { - this.tomcat = new Tomcat(); - this.tomcat.getConnector().setPort(0); - Context context = this.tomcat.addContext("/", null); - this.tomcat.addServlet("/", "test", new TestServlet()); - context.addServletMappingDecoded("/", "test"); - this.tomcat.addServlet("/", "set-cookie", new CookiesServlet()); - context.addServletMappingDecoded("/set-cookie", "set-cookie"); - this.tomcat.addServlet("/", "query-parameter", new QueryParameterServlet()); - context.addServletMappingDecoded("/query-parameter", "query-parameter"); - this.tomcat.addServlet("/", "form-url-encoded", new FormUrlEncodedServlet()); - context.addServletMappingDecoded("/form-url-encoded", "form-url-encoded"); - this.tomcat.start(); - this.port = this.tomcat.getConnector().getLocalPort(); + public void beforeAll(ExtensionContext extensionContext) { + Store store = extensionContext.getStore(Namespace.create(TomcatServer.class)); + store.getOrComputeIfAbsent(Tomcat.class, (key) -> { + Tomcat tomcat = new Tomcat(); + tomcat.getConnector().setPort(0); + Context context = tomcat.addContext("/", null); + tomcat.addServlet("/", "test", new TestServlet()); + context.addServletMappingDecoded("/", "test"); + tomcat.addServlet("/", "set-cookie", new CookiesServlet()); + context.addServletMappingDecoded("/set-cookie", "set-cookie"); + tomcat.addServlet("/", "query-parameter", new QueryParameterServlet()); + context.addServletMappingDecoded("/query-parameter", "query-parameter"); + tomcat.addServlet("/", "form-url-encoded", new FormUrlEncodedServlet()); + context.addServletMappingDecoded("/form-url-encoded", "form-url-encoded"); + try { + tomcat.start(); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + this.port = tomcat.getConnector().getLocalPort(); + return tomcat; + }); } @Override - protected void after() { - try { - this.tomcat.stop(); - } - catch (LifecycleException ex) { - throw new RuntimeException(ex); + public void afterAll(ExtensionContext extensionContext) throws LifecycleException { + Store store = extensionContext.getStore(Namespace.create(TomcatServer.class)); + Tomcat tomcat = store.get(Tomcat.class, Tomcat.class); + if (tomcat != null) { + tomcat.stop(); } } diff --git a/spring-restdocs-webtestclient/build.gradle b/spring-restdocs-webtestclient/build.gradle index eadcc88c..951e41f5 100644 --- a/spring-restdocs-webtestclient/build.gradle +++ b/spring-restdocs-webtestclient/build.gradle @@ -13,10 +13,10 @@ dependencies { internal(platform(project(":spring-restdocs-platform"))) testImplementation(testFixtures(project(":spring-restdocs-core"))) - testImplementation("junit:junit") - testImplementation("org.assertj:assertj-core") - testImplementation("org.hamcrest:hamcrest-library") - testImplementation("org.mockito:mockito-core") testRuntimeOnly("org.springframework:spring-context") } + +tasks.named("test") { + useJUnitPlatform(); +} diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java index 5c8782e3..1087ea41 100644 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java +++ b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java @@ -20,7 +20,7 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.ContentDisposition; @@ -48,12 +48,12 @@ * * @author Andy Wilkinson */ -public class WebTestClientRequestConverterTests { +class WebTestClientRequestConverterTests { private final WebTestClientRequestConverter converter = new WebTestClientRequestConverter(); @Test - public void httpRequest() { + void httpRequest() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) .configureClient() .baseUrl("http://localhost/") @@ -69,7 +69,7 @@ public void httpRequest() { } @Test - public void httpRequestWithCustomPort() { + void httpRequestWithCustomPort() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) .configureClient() .baseUrl("http://localhost:8080/") @@ -85,7 +85,7 @@ public void httpRequestWithCustomPort() { } @Test - public void requestWithHeaders() { + void requestWithHeaders() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/"), (req) -> null)) .configureClient() .baseUrl("http://localhost/") @@ -105,7 +105,7 @@ public void requestWithHeaders() { } @Test - public void httpsRequest() { + void httpsRequest() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) .configureClient() .baseUrl("https://localhost/") @@ -121,7 +121,7 @@ public void httpsRequest() { } @Test - public void httpsRequestWithCustomPort() { + void httpsRequestWithCustomPort() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) .configureClient() .baseUrl("https://localhost:8443/") @@ -137,7 +137,7 @@ public void httpsRequestWithCustomPort() { } @Test - public void getRequestWithQueryString() { + void getRequestWithQueryString() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) .configureClient() .baseUrl("http://localhost/") @@ -153,7 +153,7 @@ public void getRequestWithQueryString() { } @Test - public void postRequestWithFormDataParameters() { + void postRequestWithFormDataParameters() { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.addAll("a", Arrays.asList("alpha", "apple")); parameters.addAll("b", Arrays.asList("br&vo")); @@ -181,7 +181,7 @@ public void postRequestWithFormDataParameters() { } @Test - public void postRequestWithQueryStringParameters() { + void postRequestWithQueryStringParameters() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> { req.body(BodyExtractors.toFormData()).block(); return null; @@ -200,7 +200,7 @@ public void postRequestWithQueryStringParameters() { } @Test - public void postRequestWithQueryStringAndFormDataParameters() { + void postRequestWithQueryStringAndFormDataParameters() { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.addAll("a", Arrays.asList("apple")); ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> { @@ -227,7 +227,7 @@ public void postRequestWithQueryStringAndFormDataParameters() { } @Test - public void postRequestWithNoContentType() { + void postRequestWithNoContentType() { ExchangeResult result = WebTestClient .bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> ServerResponse.ok().build())) .configureClient() @@ -244,7 +244,7 @@ public void postRequestWithNoContentType() { } @Test - public void multipartUpload() { + void multipartUpload() { MultiValueMap multipartData = new LinkedMultiValueMap<>(); multipartData.add("file", new byte[] { 1, 2, 3, 4 }); ExchangeResult result = WebTestClient @@ -274,7 +274,7 @@ public void multipartUpload() { } @Test - public void multipartUploadFromResource() { + void multipartUploadFromResource() { MultiValueMap multipartData = new LinkedMultiValueMap<>(); multipartData.add("file", new ByteArrayResource(new byte[] { 1, 2, 3, 4 }) { @@ -314,7 +314,7 @@ public String getFilename() { } @Test - public void requestWithCookies() { + void requestWithCookies() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) .configureClient() .baseUrl("http://localhost/") diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java index 474c6feb..e50fc5a5 100644 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java +++ b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java @@ -18,7 +18,7 @@ import java.util.Collections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -40,12 +40,12 @@ * * @author Andy Wilkinson */ -public class WebTestClientResponseConverterTests { +class WebTestClientResponseConverterTests { private final WebTestClientResponseConverter converter = new WebTestClientResponseConverter(); @Test - public void basicResponse() { + void basicResponse() { ExchangeResult result = WebTestClient .bindToRouterFunction( RouterFunctions.route(GET("/foo"), (req) -> ServerResponse.ok().bodyValue("Hello, World!"))) @@ -66,7 +66,7 @@ public void basicResponse() { } @Test - public void responseWithCookie() { + void responseWithCookie() { ExchangeResult result = WebTestClient .bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> ServerResponse.ok() @@ -92,7 +92,7 @@ public void responseWithCookie() { } @Test - public void responseWithNonStandardStatusCode() { + void responseWithNonStandardStatusCode() { ExchangeResult result = WebTestClient .bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> ServerResponse.status(210).build())) .configureClient() diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java index aaf87316..efa22ebf 100644 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java +++ b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,12 +18,14 @@ import java.net.URI; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.springframework.http.HttpMethod; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ExchangeFunction; @@ -38,16 +40,19 @@ * * @author Andy Wilkinson */ -public class WebTestClientRestDocumentationConfigurerTests { +@ExtendWith(RestDocumentationExtension.class) +class WebTestClientRestDocumentationConfigurerTests { - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); + private WebTestClientRestDocumentationConfigurer configurer; - private final WebTestClientRestDocumentationConfigurer configurer = new WebTestClientRestDocumentationConfigurer( - this.restDocumentation); + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { + this.configurer = new WebTestClientRestDocumentationConfigurer(restDocumentation); + + } @Test - public void configurationCanBeRetrievedButOnlyOnce() { + void configurationCanBeRetrievedButOnlyOnce() { ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("/test")) .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1") .build(); @@ -58,7 +63,7 @@ public void configurationCanBeRetrievedButOnlyOnce() { } @Test - public void requestUriHasDefaultsAppliedWhenItHasNoHost() { + void requestUriHasDefaultsAppliedWhenItHasNoHost() { ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("/test?foo=bar#baz")) .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1") .build(); @@ -70,7 +75,7 @@ public void requestUriHasDefaultsAppliedWhenItHasNoHost() { } @Test - public void requestUriIsNotChangedWhenItHasAHost() { + void requestUriIsNotChangedWhenItHasAHost() { ClientRequest request = ClientRequest .create(HttpMethod.GET, URI.create("https://api.example.com:4567/test?foo=bar#baz")) .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1") diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java index 863e95b5..30db5c4a 100644 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java +++ b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -30,15 +30,16 @@ import java.util.stream.Stream; import org.assertj.core.api.Condition; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseCookie; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.templates.TemplateFormat; import org.springframework.restdocs.templates.TemplateFormats; import org.springframework.restdocs.testfixtures.SnippetConditions; @@ -75,15 +76,13 @@ * * @author Andy Wilkinson */ +@ExtendWith(RestDocumentationExtension.class) public class WebTestClientRestDocumentationIntegrationTests { - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - private WebTestClient webTestClient; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { RouterFunction route = RouterFunctions .route(RequestPredicates.GET("/"), (request) -> ServerResponse.status(HttpStatus.OK).body(fromValue(new Person("Jane", "Doe")))) @@ -99,12 +98,12 @@ public void setUp() { this.webTestClient = WebTestClient.bindToRouterFunction(route) .configureClient() .baseUrl("https://api.example.com/") - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .build(); } @Test - public void defaultSnippetGeneration() { + void defaultSnippetGeneration() { File outputDir = new File("build/generated-snippets/default-snippets"); FileSystemUtils.deleteRecursively(outputDir); this.webTestClient.get() @@ -119,7 +118,7 @@ public void defaultSnippetGeneration() { } @Test - public void pathParametersSnippet() { + void pathParametersSnippet() { this.webTestClient.get() .uri("/{foo}/{bar}", "1", "2") .exchange() @@ -136,7 +135,7 @@ public void pathParametersSnippet() { } @Test - public void queryParametersSnippet() { + void queryParametersSnippet() { this.webTestClient.get() .uri("/?a=alpha&b=bravo") .exchange() @@ -153,7 +152,7 @@ public void queryParametersSnippet() { } @Test - public void multipart() { + void multipart() { MultiValueMap multipartData = new LinkedMultiValueMap<>(); multipartData.add("a", "alpha"); multipartData.add("b", "bravo"); @@ -173,7 +172,7 @@ public void multipart() { } @Test - public void responseWithSetCookie() { + void responseWithSetCookie() { this.webTestClient.get() .uri("/set-cookie") .exchange() @@ -187,7 +186,7 @@ public void responseWithSetCookie() { } @Test - public void curlSnippetWithCookies() { + void curlSnippetWithCookies() { this.webTestClient.get() .uri("/") .cookie("cookieName", "cookieVal") @@ -204,7 +203,7 @@ public void curlSnippetWithCookies() { } @Test - public void curlSnippetWithEmptyParameterQueryString() { + void curlSnippetWithEmptyParameterQueryString() { this.webTestClient.get() .uri("/?a=") .accept(MediaType.APPLICATION_JSON) @@ -220,7 +219,7 @@ public void curlSnippetWithEmptyParameterQueryString() { } @Test - public void httpieSnippetWithCookies() { + void httpieSnippetWithCookies() { this.webTestClient.get() .uri("/") .cookie("cookieName", "cookieVal") @@ -237,7 +236,7 @@ public void httpieSnippetWithCookies() { } @Test - public void illegalStateExceptionShouldBeThrownWhenCallDocumentWebClientNotConfigured() { + void illegalStateExceptionShouldBeThrownWhenCallDocumentWebClientNotConfigured() { assertThatThrownBy(() -> this.webTestClient .mutateWith((builder, httpHandlerBuilder, connector) -> builder.filters(List::clear).build()) .get() From b82451e52701743369ca055365ef869f2d97a542 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Jun 2025 17:35:46 +0100 Subject: [PATCH 10/15] Remove support for JUnit 4 Closes gh-958 --- docs/build.gradle | 1 - docs/src/docs/asciidoc/getting-started.adoc | 82 ++----------------- .../CustomDefaultOperationPreprocessors.java | 21 +++-- .../mockmvc/CustomDefaultSnippets.java | 21 +++-- .../com/example/mockmvc/CustomEncoding.java | 21 +++-- .../com/example/mockmvc/CustomFormat.java | 21 +++-- .../mockmvc/CustomUriConfiguration.java | 21 +++-- .../mockmvc/EveryTestPreprocessing.java | 19 ++--- .../ExampleApplicationJUnit5Tests.java | 45 ---------- .../mockmvc/ExampleApplicationTests.java | 27 +++--- .../example/mockmvc/ParameterizedOutput.java | 21 +++-- .../CustomDefaultOperationPreprocessors.java | 21 +++-- .../restassured/CustomDefaultSnippets.java | 21 +++-- .../example/restassured/CustomEncoding.java | 21 +++-- .../com/example/restassured/CustomFormat.java | 21 +++-- .../restassured/EveryTestPreprocessing.java | 23 +++--- .../ExampleApplicationJUnit5Tests.java | 43 ---------- .../restassured/ExampleApplicationTests.java | 21 +++-- .../restassured/ParameterizedOutput.java | 19 ++--- .../CustomDefaultOperationPreprocessors.java | 21 +++-- .../webtestclient/CustomDefaultSnippets.java | 21 +++-- .../example/webtestclient/CustomEncoding.java | 21 +++-- .../example/webtestclient/CustomFormat.java | 21 +++-- .../webtestclient/CustomUriConfiguration.java | 21 +++-- .../webtestclient/EveryTestPreprocessing.java | 23 +++--- .../ExampleApplicationJUnit5Tests.java | 45 ---------- .../ExampleApplicationTests.java | 27 +++--- .../webtestclient/ParameterizedOutput.java | 21 +++-- spring-restdocs-core/build.gradle | 1 - .../restdocs/JUnitRestDocumentation.java | 77 ----------------- .../restdocs/ManualRestDocumentation.java | 8 +- spring-restdocs-platform/build.gradle | 1 - 32 files changed, 244 insertions(+), 554 deletions(-) delete mode 100644 docs/src/test/java/com/example/mockmvc/ExampleApplicationJUnit5Tests.java delete mode 100644 docs/src/test/java/com/example/restassured/ExampleApplicationJUnit5Tests.java delete mode 100644 docs/src/test/java/com/example/webtestclient/ExampleApplicationJUnit5Tests.java delete mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/JUnitRestDocumentation.java diff --git a/docs/build.gradle b/docs/build.gradle index 8c08f180..fefe9a5f 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -18,7 +18,6 @@ dependencies { testImplementation(project(":spring-restdocs-webtestclient")) testImplementation("jakarta.servlet:jakarta.servlet-api") testImplementation("jakarta.validation:jakarta.validation-api") - testImplementation("junit:junit") testImplementation("org.testng:testng:6.9.10") testImplementation("org.junit.jupiter:junit-jupiter-api") } diff --git a/docs/src/docs/asciidoc/getting-started.adoc b/docs/src/docs/asciidoc/getting-started.adoc index afa220a0..11be6100 100644 --- a/docs/src/docs/asciidoc/getting-started.adoc +++ b/docs/src/docs/asciidoc/getting-started.adoc @@ -203,8 +203,7 @@ It then produces documentation snippets for the request and the resulting respon ==== Setting up Your Tests Exactly how you set up your tests depends on the test framework that you use. -Spring REST Docs provides first-class support for JUnit 5 and JUnit 4. -JUnit 5 is recommended. +Spring REST Docs provides first-class support for JUnit 5. Other frameworks, such as TestNG, are also supported, although slightly more setup is required. @@ -258,72 +257,6 @@ public class JUnit5ExampleTests { Next, you must provide a `@BeforeEach` method to configure MockMvc or WebTestClient, or REST Assured. The following listings show how to do so: -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/ExampleApplicationJUnit5Tests.java[tags=setup] ----- -<1> The `MockMvc` instance is configured by using a `MockMvcRestDocumentationConfigurer`. -You can obtain an instance of this class from the static `documentationConfiguration()` method on `org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/ExampleApplicationJUnit5Tests.java[tags=setup] ----- -<1> The `WebTestClient` instance is configured by adding a `WebTestClientRestDocumentationConfigurer` as an `ExchangeFilterFunction`. -You can obtain an instance of this class from the static `documentationConfiguration()` method on `org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/ExampleApplicationJUnit5Tests.java[tags=setup] ----- -<1> REST Assured is configured by adding a `RestAssuredRestDocumentationConfigurer` as a `Filter`. -You can obtain an instance of this class from the static `documentationConfiguration()` method on `RestAssuredRestDocumentation` in the `org.springframework.restdocs.restassured` package. - -The configurer applies sensible defaults and also provides an API for customizing the configuration. -See the <> for more information. - - - -[[getting-started-documentation-snippets-setup-junit]] -===== Setting up Your JUnit 4 Tests - -When using JUnit 4, the first step in generating documentation snippets is to declare a `public` `JUnitRestDocumentation` field that is annotated as a JUnit `@Rule`. -The following example shows how to do so: - -[source,java,indent=0] ----- -@Rule -public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); ----- - -By default, the `JUnitRestDocumentation` rule is automatically configured with an output directory based on your project's build tool: - -[cols="2,5"] -|=== -| Build tool | Output directory - -| Maven -| `target/generated-snippets` - -| Gradle -| `build/generated-snippets` -|=== - -You can override the default by providing an output directory when you create the `JUnitRestDocumentation` instance. -The following example shows how to do so: - -[source,java,indent=0] ----- -@Rule -public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("custom"); ----- - -Next, you must provide an `@Before` method to configure MockMvc or WebTestClient, or REST Assured. -The following examples show how to do so: - [source,java,indent=0,role="primary"] .MockMvc ---- @@ -337,7 +270,7 @@ You can obtain an instance of this class from the static `documentationConfigura ---- include::{examples-dir}/com/example/webtestclient/ExampleApplicationTests.java[tags=setup] ---- -<1> The `WebTestClient` instance is configured by adding a `WebTestclientRestDocumentationConfigurer` as an `ExchangeFilterFunction`. +<1> The `WebTestClient` instance is configured by adding a `WebTestClientRestDocumentationConfigurer` as an `ExchangeFilterFunction`. You can obtain an instance of this class from the static `documentationConfiguration()` method on `org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation`. [source,java,indent=0,role="secondary"] @@ -353,16 +286,15 @@ See the <> for more information. + [[getting-started-documentation-snippets-setup-manual]] ===== Setting up your tests without JUnit -The configuration when JUnit is not being used is largely similar to when it is being used. -This section describes the key differences. -The {samples}/testng[TestNG sample] also illustrates the approach. +The configuration when JUnit is not being used is a little more involved as the test class must perform some lifecycle management. +The {samples}/testng[TestNG sample] illustrates the approach. -The first difference is that you should use `ManualRestDocumentation` in place of `JUnitRestDocumentation`. -Also, you do not need the `@Rule` annotation. -The following example shows how to use `ManualRestDocumentation`: +First, you need a `ManualRestDocumentation` field. +The following example shows how to define it: [source,java,indent=0] ---- diff --git a/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java b/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java index c8366077..5aec4248 100644 --- a/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java +++ b/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,10 +16,11 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @@ -28,21 +29,19 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -public class CustomDefaultOperationPreprocessors { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomDefaultOperationPreprocessors { private WebApplicationContext context; @SuppressWarnings("unused") private MockMvc mockMvc; - @Before - public void setup() { + @BeforeEach + void setup(RestDocumentationContextProvider restDocumentation) { // tag::custom-default-operation-preprocessors[] this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).operationPreprocessors() + .apply(documentationConfiguration(restDocumentation).operationPreprocessors() .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> .withResponseDefaults(prettyPrint())) // <2> .build(); diff --git a/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java b/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java index e5905a6e..860e8e1e 100644 --- a/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java +++ b/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,11 +16,12 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @@ -28,10 +29,8 @@ import static org.springframework.restdocs.cli.CliDocumentation.curlRequest; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -public class CustomDefaultSnippets { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomDefaultSnippets { @Autowired private WebApplicationContext context; @@ -39,11 +38,11 @@ public class CustomDefaultSnippets { @SuppressWarnings("unused") private MockMvc mockMvc; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-default-snippets[] this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).snippets().withDefaults(curlRequest())) + .apply(documentationConfiguration(restDocumentation).snippets().withDefaults(curlRequest())) .build(); // end::custom-default-snippets[] } diff --git a/docs/src/test/java/com/example/mockmvc/CustomEncoding.java b/docs/src/test/java/com/example/mockmvc/CustomEncoding.java index fbca4c61..7b6d1a42 100644 --- a/docs/src/test/java/com/example/mockmvc/CustomEncoding.java +++ b/docs/src/test/java/com/example/mockmvc/CustomEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,21 +16,20 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -public class CustomEncoding { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomEncoding { @Autowired private WebApplicationContext context; @@ -38,11 +37,11 @@ public class CustomEncoding { @SuppressWarnings("unused") private MockMvc mockMvc; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-encoding[] this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).snippets().withEncoding("ISO-8859-1")) + .apply(documentationConfiguration(restDocumentation).snippets().withEncoding("ISO-8859-1")) .build(); // end::custom-encoding[] } diff --git a/docs/src/test/java/com/example/mockmvc/CustomFormat.java b/docs/src/test/java/com/example/mockmvc/CustomFormat.java index 7427491f..75a30465 100644 --- a/docs/src/test/java/com/example/mockmvc/CustomFormat.java +++ b/docs/src/test/java/com/example/mockmvc/CustomFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,11 +16,12 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.templates.TemplateFormats; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -28,10 +29,8 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -public class CustomFormat { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomFormat { @Autowired private WebApplicationContext context; @@ -39,11 +38,11 @@ public class CustomFormat { @SuppressWarnings("unused") private MockMvc mockMvc; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-format[] this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).snippets() + .apply(documentationConfiguration(restDocumentation).snippets() .withTemplateFormat(TemplateFormats.markdown())) .build(); // end::custom-format[] diff --git a/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java b/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java index 7b3a1ef9..7af440b0 100644 --- a/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java +++ b/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,21 +16,20 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -public class CustomUriConfiguration { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomUriConfiguration { @Autowired private WebApplicationContext context; @@ -38,11 +37,11 @@ public class CustomUriConfiguration { @SuppressWarnings("unused") private MockMvc mockMvc; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-uri-configuration[] this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).uris() + .apply(documentationConfiguration(restDocumentation).uris() .withScheme("https") .withHost("example.com") .withPort(443)) diff --git a/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java b/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java index 7c27c235..dcf4d44a 100644 --- a/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java +++ b/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,10 +16,11 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @@ -33,20 +34,18 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@ExtendWith(RestDocumentationExtension.class) public class EveryTestPreprocessing { - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - private WebApplicationContext context; // tag::setup[] private MockMvc mockMvc; - @Before - public void setup() { + @BeforeEach + void setup(RestDocumentationContextProvider restDocumentation) { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).operationPreprocessors() + .apply(documentationConfiguration(restDocumentation).operationPreprocessors() .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> .withResponseDefaults(prettyPrint())) // <2> .build(); diff --git a/docs/src/test/java/com/example/mockmvc/ExampleApplicationJUnit5Tests.java b/docs/src/test/java/com/example/mockmvc/ExampleApplicationJUnit5Tests.java deleted file mode 100644 index c012699e..00000000 --- a/docs/src/test/java/com/example/mockmvc/ExampleApplicationJUnit5Tests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ExampleApplicationJUnit5Tests { - - @SuppressWarnings("unused") - // tag::setup[] - private MockMvc mockMvc; - - @BeforeEach - void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) - .apply(documentationConfiguration(restDocumentation)) // <1> - .build(); - } - // end::setup[] - -} diff --git a/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java b/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java index 3e664785..24c0a3f4 100644 --- a/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java +++ b/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,33 +16,28 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -public class ExampleApplicationTests { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class ExampleApplicationTests { @SuppressWarnings("unused") // tag::setup[] private MockMvc mockMvc; - @Autowired - private WebApplicationContext context; - - @Before - public void setUp() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) // <1> + @BeforeEach + void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) { + this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) + .apply(documentationConfiguration(restDocumentation)) // <1> .build(); } // end::setup[] diff --git a/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java b/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java index 43664322..831c9a4a 100644 --- a/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java +++ b/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,10 +16,11 @@ package com.example.mockmvc; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @@ -27,10 +28,8 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -public class ParameterizedOutput { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class ParameterizedOutput { @SuppressWarnings("unused") private MockMvc mockMvc; @@ -38,10 +37,10 @@ public class ParameterizedOutput { private WebApplicationContext context; // tag::parameterized-output[] - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) + .apply(documentationConfiguration(restDocumentation)) .alwaysDo(document("{method-name}/{step}/")) .build(); } diff --git a/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java b/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java index 17172333..2d0fa772 100644 --- a/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java +++ b/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,28 +18,27 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; -public class CustomDefaultOperationPreprocessors { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomDefaultOperationPreprocessors { @SuppressWarnings("unused") private RequestSpecification spec; - @Before - public void setup() { + @BeforeEach + void setup(RestDocumentationContextProvider restDocumentation) { // tag::custom-default-operation-preprocessors[] this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(this.restDocumentation).operationPreprocessors() + .addFilter(documentationConfiguration(restDocumentation).operationPreprocessors() .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> .withResponseDefaults(prettyPrint())) // <2> .build(); diff --git a/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java b/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java index 2edf5a20..3b6d175f 100644 --- a/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java +++ b/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,27 +18,26 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import static org.springframework.restdocs.cli.CliDocumentation.curlRequest; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; -public class CustomDefaultSnippets { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomDefaultSnippets { @SuppressWarnings("unused") private RequestSpecification spec; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-default-snippets[] this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(this.restDocumentation).snippets().withDefaults(curlRequest())) + .addFilter(documentationConfiguration(restDocumentation).snippets().withDefaults(curlRequest())) .build(); // end::custom-default-snippets[] } diff --git a/docs/src/test/java/com/example/restassured/CustomEncoding.java b/docs/src/test/java/com/example/restassured/CustomEncoding.java index 2beaa7bb..316b19a2 100644 --- a/docs/src/test/java/com/example/restassured/CustomEncoding.java +++ b/docs/src/test/java/com/example/restassured/CustomEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,26 +18,25 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; -public class CustomEncoding { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomEncoding { @SuppressWarnings("unused") private RequestSpecification spec; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-encoding[] this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(this.restDocumentation).snippets().withEncoding("ISO-8859-1")) + .addFilter(documentationConfiguration(restDocumentation).snippets().withEncoding("ISO-8859-1")) .build(); // end::custom-encoding[] } diff --git a/docs/src/test/java/com/example/restassured/CustomFormat.java b/docs/src/test/java/com/example/restassured/CustomFormat.java index 33fd0661..5f690f1b 100644 --- a/docs/src/test/java/com/example/restassured/CustomFormat.java +++ b/docs/src/test/java/com/example/restassured/CustomFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,27 +18,26 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.templates.TemplateFormats; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; -public class CustomFormat { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomFormat { @SuppressWarnings("unused") private RequestSpecification spec; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-format[] this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(this.restDocumentation).snippets() + .addFilter(documentationConfiguration(restDocumentation).snippets() .withTemplateFormat(TemplateFormats.markdown())) .build(); // end::custom-format[] diff --git a/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java b/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java index 3257de4f..741cfdfe 100644 --- a/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java +++ b/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -19,10 +19,11 @@ import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import static org.hamcrest.CoreMatchers.is; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; @@ -32,25 +33,23 @@ import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; -public class EveryTestPreprocessing { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class EveryTestPreprocessing { // tag::setup[] private RequestSpecification spec; - @Before - public void setup() { + @BeforeEach + void setup(RestDocumentationContextProvider restDocumentation) { this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(this.restDocumentation).operationPreprocessors() + .addFilter(documentationConfiguration(restDocumentation).operationPreprocessors() .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> .withResponseDefaults(prettyPrint())) // <2> .build(); } // end::setup[] - public void use() { + void use() { // tag::use[] RestAssured.given(this.spec) .filter(document("index", links(linkWithRel("self").description("Canonical self link")))) diff --git a/docs/src/test/java/com/example/restassured/ExampleApplicationJUnit5Tests.java b/docs/src/test/java/com/example/restassured/ExampleApplicationJUnit5Tests.java deleted file mode 100644 index fe83e2a7..00000000 --- a/docs/src/test/java/com/example/restassured/ExampleApplicationJUnit5Tests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ExampleApplicationJUnit5Tests { - - @SuppressWarnings("unused") - // tag::setup[] - private RequestSpecification spec; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(restDocumentation)) // <1> - .build(); - } - // end::setup[] - -} diff --git a/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java b/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java index 377df1ad..7affb1ef 100644 --- a/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java +++ b/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,25 +18,24 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; -public class ExampleApplicationTests { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class ExampleApplicationTests { @SuppressWarnings("unused") // tag::setup[] private RequestSpecification spec; - @Before - public void setUp() { - this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(this.restDocumentation)) // <1> + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { + this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(restDocumentation)) // <1> .build(); } // end::setup[] diff --git a/docs/src/test/java/com/example/restassured/ParameterizedOutput.java b/docs/src/test/java/com/example/restassured/ParameterizedOutput.java index 228e6188..7471d177 100644 --- a/docs/src/test/java/com/example/restassured/ParameterizedOutput.java +++ b/docs/src/test/java/com/example/restassured/ParameterizedOutput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -18,26 +18,25 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; +@ExtendWith(RestDocumentationExtension.class) public class ParameterizedOutput { - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - @SuppressWarnings("unused") private RequestSpecification spec; // tag::parameterized-output[] - @Before - public void setUp() { - this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(this.restDocumentation)) + @BeforeEach + public void setUp(RestDocumentationContextProvider restDocumentation) { + this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(restDocumentation)) .addFilter(document("{method-name}/{step}")) .build(); } diff --git a/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java b/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java index cb6d6974..17ee06b4 100644 --- a/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java +++ b/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -16,35 +16,34 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class CustomDefaultOperationPreprocessors { +@ExtendWith(RestDocumentationExtension.class) +class CustomDefaultOperationPreprocessors { // @formatter:off - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - private ApplicationContext context; @SuppressWarnings("unused") private WebTestClient webTestClient; - @Before - public void setup() { + @BeforeEach + void setup(RestDocumentationContextProvider restDocumentation) { // tag::custom-default-operation-preprocessors[] this.webTestClient = WebTestClient.bindToApplicationContext(this.context) .configureClient() - .filter(documentationConfiguration(this.restDocumentation) + .filter(documentationConfiguration(restDocumentation) .operationPreprocessors() .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> .withResponseDefaults(prettyPrint())) // <2> diff --git a/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java b/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java index 5910ac71..420a52f4 100644 --- a/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java +++ b/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -16,36 +16,35 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.cli.CliDocumentation.curlRequest; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class CustomDefaultSnippets { +@ExtendWith(RestDocumentationExtension.class) +class CustomDefaultSnippets { // @formatter:off - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - @Autowired private ApplicationContext context; @SuppressWarnings("unused") private WebTestClient webTestClient; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-default-snippets[] this.webTestClient = WebTestClient.bindToApplicationContext(this.context) .configureClient().filter( - documentationConfiguration(this.restDocumentation) + documentationConfiguration(restDocumentation) .snippets().withDefaults(curlRequest())) .build(); // end::custom-default-snippets[] diff --git a/docs/src/test/java/com/example/webtestclient/CustomEncoding.java b/docs/src/test/java/com/example/webtestclient/CustomEncoding.java index fd817671..42800d79 100644 --- a/docs/src/test/java/com/example/webtestclient/CustomEncoding.java +++ b/docs/src/test/java/com/example/webtestclient/CustomEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -16,34 +16,33 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class CustomEncoding { +@ExtendWith(RestDocumentationExtension.class) +class CustomEncoding { // @formatter:off - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - @Autowired private ApplicationContext context; @SuppressWarnings("unused") private WebTestClient webTestClient; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-encoding[] this.webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient() - .filter(documentationConfiguration(this.restDocumentation) + .filter(documentationConfiguration(restDocumentation) .snippets().withEncoding("ISO-8859-1")) .build(); // end::custom-encoding[] diff --git a/docs/src/test/java/com/example/webtestclient/CustomFormat.java b/docs/src/test/java/com/example/webtestclient/CustomFormat.java index 9021a39a..ab57f184 100644 --- a/docs/src/test/java/com/example/webtestclient/CustomFormat.java +++ b/docs/src/test/java/com/example/webtestclient/CustomFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -16,35 +16,34 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.templates.TemplateFormats; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class CustomFormat { +@ExtendWith(RestDocumentationExtension.class) +class CustomFormat { // @formatter:off - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - @Autowired private ApplicationContext context; @SuppressWarnings("unused") private WebTestClient webTestClient; - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { // tag::custom-format[] this.webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient() - .filter(documentationConfiguration(this.restDocumentation) + .filter(documentationConfiguration(restDocumentation) .snippets().withTemplateFormat(TemplateFormats.markdown())) .build(); // end::custom-format[] diff --git a/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java b/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java index 94d84247..9db88cde 100644 --- a/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java +++ b/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,20 +16,19 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class CustomUriConfiguration { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class CustomUriConfiguration { @SuppressWarnings("unused") private WebTestClient webTestClient; @@ -38,12 +37,12 @@ public class CustomUriConfiguration { private ApplicationContext context; // tag::custom-uri-configuration[] - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { this.webTestClient = WebTestClient.bindToApplicationContext(this.context) .configureClient() .baseUrl("https://api.example.com/") // <1> - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .build(); } // end::custom-uri-configuration[] diff --git a/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java b/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java index 97240a95..e6ff7ceb 100644 --- a/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java +++ b/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 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. @@ -16,11 +16,12 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; @@ -30,23 +31,21 @@ import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class EveryTestPreprocessing { +@ExtendWith(RestDocumentationExtension.class) +class EveryTestPreprocessing { // @formatter:off - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); - private ApplicationContext context; // tag::setup[] private WebTestClient webTestClient; - @Before - public void setup() { + @BeforeEach + void setup(RestDocumentationContextProvider restDocumentation) { this.webTestClient = WebTestClient.bindToApplicationContext(this.context) .configureClient() - .filter(documentationConfiguration(this.restDocumentation) + .filter(documentationConfiguration(restDocumentation) .operationPreprocessors() .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> .withResponseDefaults(prettyPrint())) // <2> @@ -54,7 +53,7 @@ public void setup() { } // end::setup[] - public void use() { + void use() { // tag::use[] this.webTestClient.get().uri("/").exchange().expectStatus().isOk() .expectBody().consumeWith(document("index", diff --git a/docs/src/test/java/com/example/webtestclient/ExampleApplicationJUnit5Tests.java b/docs/src/test/java/com/example/webtestclient/ExampleApplicationJUnit5Tests.java deleted file mode 100644 index 4739440f..00000000 --- a/docs/src/test/java/com/example/webtestclient/ExampleApplicationJUnit5Tests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2023 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ExampleApplicationJUnit5Tests { - - @SuppressWarnings("unused") - // tag::setup[] - private WebTestClient webTestClient; - - @BeforeEach - void setUp(ApplicationContext applicationContext, RestDocumentationContextProvider restDocumentation) { - this.webTestClient = WebTestClient.bindToApplicationContext(applicationContext) - .configureClient() - .filter(documentationConfiguration(restDocumentation)) // <1> - .build(); - } - // end::setup[] - -} diff --git a/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java b/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java index 0ec72a4e..3126487a 100644 --- a/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java +++ b/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,33 +16,28 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class ExampleApplicationTests { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class ExampleApplicationTests { @SuppressWarnings("unused") // tag::setup[] private WebTestClient webTestClient; - @Autowired - private ApplicationContext context; - - @Before - public void setUp() { - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) + @BeforeEach + void setUp(ApplicationContext applicationContext, RestDocumentationContextProvider restDocumentation) { + this.webTestClient = WebTestClient.bindToApplicationContext(applicationContext) .configureClient() - .filter(documentationConfiguration(this.restDocumentation)) // <1> + .filter(documentationConfiguration(restDocumentation)) // <1> .build(); } // end::setup[] diff --git a/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java b/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java index 6d1dae31..f3770274 100644 --- a/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java +++ b/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2025 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. @@ -16,21 +16,20 @@ package com.example.webtestclient; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.JUnitRestDocumentation; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.web.reactive.server.WebTestClient; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -public class ParameterizedOutput { - - @Rule - public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); +@ExtendWith(RestDocumentationExtension.class) +class ParameterizedOutput { @SuppressWarnings("unused") private WebTestClient webTestClient; @@ -39,11 +38,11 @@ public class ParameterizedOutput { private ApplicationContext context; // tag::parameterized-output[] - @Before - public void setUp() { + @BeforeEach + void setUp(RestDocumentationContextProvider restDocumentation) { this.webTestClient = WebTestClient.bindToApplicationContext(this.context) .configureClient() - .filter(documentationConfiguration(this.restDocumentation)) + .filter(documentationConfiguration(restDocumentation)) .entityExchangeResultConsumer(document("{method-name}/{step}")) .build(); } diff --git a/spring-restdocs-core/build.gradle b/spring-restdocs-core/build.gradle index 6832649b..496bb1a3 100644 --- a/spring-restdocs-core/build.gradle +++ b/spring-restdocs-core/build.gradle @@ -47,7 +47,6 @@ dependencies { optional(platform(project(":spring-restdocs-platform"))) optional("jakarta.validation:jakarta.validation-api") - optional("junit:junit") optional("org.hibernate.validator:hibernate-validator") optional("org.junit.jupiter:junit-jupiter-api") diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/JUnitRestDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/JUnitRestDocumentation.java deleted file mode 100644 index bbf99d94..00000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/JUnitRestDocumentation.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014-2019 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -/** - * A JUnit {@link TestRule} used to automatically manage the - * {@link RestDocumentationContext}. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public class JUnitRestDocumentation implements RestDocumentationContextProvider, TestRule { - - private final ManualRestDocumentation delegate; - - /** - * Creates a new {@code JUnitRestDocumentation} instance that will generate snippets - * to <gradle/maven build path>/generated-snippet. - */ - public JUnitRestDocumentation() { - this.delegate = new ManualRestDocumentation(); - } - - /** - * Creates a new {@code JUnitRestDocumentation} instance that will generate snippets - * to the given {@code outputDirectory}. - * @param outputDirectory the output directory - */ - public JUnitRestDocumentation(String outputDirectory) { - this.delegate = new ManualRestDocumentation(outputDirectory); - } - - @Override - public Statement apply(final Statement base, final Description description) { - return new Statement() { - - @Override - public void evaluate() throws Throwable { - Class testClass = description.getTestClass(); - String methodName = description.getMethodName(); - JUnitRestDocumentation.this.delegate.beforeTest(testClass, methodName); - try { - base.evaluate(); - } - finally { - JUnitRestDocumentation.this.delegate.afterTest(); - } - } - - }; - - } - - @Override - public RestDocumentationContext beforeOperation() { - return this.delegate.beforeOperation(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java index cb56676e..03718954 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -18,13 +18,15 @@ import java.io.File; +import org.junit.jupiter.api.extension.Extension; + /** * {@code ManualRestDocumentation} is used to manually manage the * {@link RestDocumentationContext}. Primarly intended for use with TestNG, but suitable * for use in any environment where manual management of the context is required. *

- * Users of JUnit should use {@link JUnitRestDocumentation} and take advantage of its - * Rule-based support for automatic management of the context. + * Users of JUnit should use {@link RestDocumentationExtension} and take advantage of its + * {@link Extension}-based support for automatic management of the context. * * @author Andy Wilkinson * @since 1.1.0 diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle index a4928f8e..5e0404db 100644 --- a/spring-restdocs-platform/build.gradle +++ b/spring-restdocs-platform/build.gradle @@ -11,7 +11,6 @@ dependencies { api("com.samskivert:jmustache:$jmustacheVersion") api("jakarta.servlet:jakarta.servlet-api:6.1.0") api("jakarta.validation:jakarta.validation-api:3.1.0") - api("junit:junit:4.13.1") api("org.apache.pdfbox:pdfbox:2.0.27") api("org.apache.tomcat.embed:tomcat-embed-core:11.0.2") api("org.apache.tomcat.embed:tomcat-embed-el:11.0.2") From 7c7daf6c0413ecde6947eb3c10cb51114fee1f7c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Jun 2025 17:58:13 +0100 Subject: [PATCH 11/15] Close Writers in StandardWriterResolverTests The unclosed writers were preventing clean up of the JUnit-managed temporary directory. See gh-959 --- .../restdocs/snippet/StandardWriterResolverTests.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java index 13f3b6cd..0df5118d 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java @@ -78,8 +78,9 @@ void placeholdersAreResolvedInOperationName() throws IOException { PlaceholderResolver resolver = mock(PlaceholderResolver.class); given(resolver.resolvePlaceholder("a")).willReturn("alpha"); given(this.placeholderResolverFactory.create(context)).willReturn(resolver); - Writer writer = this.resolver.resolve("{a}", "bravo", context); - assertSnippetLocation(writer, new File(outputDirectory, "alpha/bravo.adoc")); + try (Writer writer = this.resolver.resolve("{a}", "bravo", context)) { + assertSnippetLocation(writer, new File(outputDirectory, "alpha/bravo.adoc")); + } } @Test @@ -89,8 +90,9 @@ void placeholdersAreResolvedInSnippetName() throws IOException { PlaceholderResolver resolver = mock(PlaceholderResolver.class); given(resolver.resolvePlaceholder("b")).willReturn("bravo"); given(this.placeholderResolverFactory.create(context)).willReturn(resolver); - Writer writer = this.resolver.resolve("alpha", "{b}", context); - assertSnippetLocation(writer, new File(outputDirectory, "alpha/bravo.adoc")); + try (Writer writer = this.resolver.resolve("alpha", "{b}", context)) { + assertSnippetLocation(writer, new File(outputDirectory, "alpha/bravo.adoc")); + } } private RestDocumentationContext createContext(String outputDir) { From 91446847681a78eb1c7a19aa663b7e50fba3ce2d Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 29 May 2025 22:24:43 +0200 Subject: [PATCH 12/15] Support link extraction with official HAL and HAL-FORMS media types Register the HalLinkExtractor for both the official HAL media type (application/vnd.hal+json) and the HAL-FORMS media type (application/prs.hal-forms+json) See gh-965 --- .../hypermedia/ContentTypeLinkExtractor.java | 7 +++++- .../restdocs/hypermedia/HalLinkExtractor.java | 2 ++ .../ContentTypeLinkExtractorTests.java | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java index ef2a9e8e..20a281de 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java @@ -30,6 +30,7 @@ * content type. * * @author Andy Wilkinson + * @author Oliver Drotbohm */ class ContentTypeLinkExtractor implements LinkExtractor { @@ -37,7 +38,11 @@ class ContentTypeLinkExtractor implements LinkExtractor { ContentTypeLinkExtractor() { this.linkExtractors.put(MediaType.APPLICATION_JSON, new AtomLinkExtractor()); - this.linkExtractors.put(HalLinkExtractor.HAL_MEDIA_TYPE, new HalLinkExtractor()); + + LinkExtractor halLinkExtractor = new HalLinkExtractor(); + this.linkExtractors.put(HalLinkExtractor.HAL_MEDIA_TYPE, halLinkExtractor); + this.linkExtractors.put(HalLinkExtractor.VND_HAL_MEDIA_TYPE, halLinkExtractor); + this.linkExtractors.put(HalLinkExtractor.HAL_FORMS_MEDIA_TYPE, halLinkExtractor); } ContentTypeLinkExtractor(Map linkExtractors) { diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java index f93c572a..82d47f0b 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java @@ -34,6 +34,8 @@ class HalLinkExtractor extends AbstractJsonLinkExtractor { static final MediaType HAL_MEDIA_TYPE = new MediaType("application", "hal+json"); + static final MediaType VND_HAL_MEDIA_TYPE = new MediaType("application", "vnd.hal+json"); + static final MediaType HAL_FORMS_MEDIA_TYPE = new MediaType("application", "prs.hal-forms+json"); @Override public Map> extractLinks(Map json) { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java index 2e51b0ac..f48bcbe4 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -28,6 +29,7 @@ import org.springframework.restdocs.operation.OperationResponse; import org.springframework.restdocs.operation.OperationResponseFactory; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -41,6 +43,8 @@ class ContentTypeLinkExtractorTests { private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + private final String halBody = "{ \"_links\" : { \"someRel\" : { \"href\" : \"someHref\" }} }"; + @Test void extractionFailsWithNullContentType() { assertThatIllegalStateException().isThrownBy(() -> new ContentTypeLinkExtractor() @@ -71,4 +75,22 @@ void extractorCalledWithCompatibleContextType() throws IOException { verify(extractor).extractLinks(response); } + @Test + public void extractsLinksFromVndHalMediaType() throws IOException { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.parseMediaType("application/vnd.hal+json")); + OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes()); + Map> links = new ContentTypeLinkExtractor().extractLinks(response); + assertThat(links).containsKey("someRel"); + } + + @Test + public void extractsLinksFromHalFormsMediaType() throws IOException { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.parseMediaType("application/prs.hal-forms+json")); + OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes()); + Map> links = new ContentTypeLinkExtractor().extractLinks(response); + assertThat(links).containsKey("someRel"); + } + } From cd16feba50652ad408786d99ea02286d850e72e3 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Jun 2025 19:24:18 +0100 Subject: [PATCH 13/15] Polish "Support link extraction with official HAL and HAL-FORMS media types" See gh-965 --- .../restdocs/hypermedia/ContentTypeLinkExtractor.java | 3 +-- .../restdocs/hypermedia/HalLinkExtractor.java | 5 ++++- .../restdocs/hypermedia/ContentTypeLinkExtractorTests.java | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java index 20a281de..bdf5772c 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2025 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. @@ -38,7 +38,6 @@ class ContentTypeLinkExtractor implements LinkExtractor { ContentTypeLinkExtractor() { this.linkExtractors.put(MediaType.APPLICATION_JSON, new AtomLinkExtractor()); - LinkExtractor halLinkExtractor = new HalLinkExtractor(); this.linkExtractors.put(HalLinkExtractor.HAL_MEDIA_TYPE, halLinkExtractor); this.linkExtractors.put(HalLinkExtractor.VND_HAL_MEDIA_TYPE, halLinkExtractor); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java index 82d47f0b..e56f32ba 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2025 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. @@ -30,11 +30,14 @@ * format. * * @author Andy Wilkinson + * @author Oliver Drotbohm */ class HalLinkExtractor extends AbstractJsonLinkExtractor { static final MediaType HAL_MEDIA_TYPE = new MediaType("application", "hal+json"); + static final MediaType VND_HAL_MEDIA_TYPE = new MediaType("application", "vnd.hal+json"); + static final MediaType HAL_FORMS_MEDIA_TYPE = new MediaType("application", "prs.hal-forms+json"); @Override diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java index f48bcbe4..eb5875f8 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java @@ -76,7 +76,7 @@ void extractorCalledWithCompatibleContextType() throws IOException { } @Test - public void extractsLinksFromVndHalMediaType() throws IOException { + void extractsLinksFromVndHalMediaType() throws IOException { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.parseMediaType("application/vnd.hal+json")); OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes()); @@ -85,7 +85,7 @@ public void extractsLinksFromVndHalMediaType() throws IOException { } @Test - public void extractsLinksFromHalFormsMediaType() throws IOException { + void extractsLinksFromHalFormsMediaType() throws IOException { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.parseMediaType("application/prs.hal-forms+json")); OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes()); From e494927b4eaa694583f3051e13658efb0ba2e1cf Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Jun 2025 19:30:44 +0100 Subject: [PATCH 14/15] Remove deprecated APIs Closes gh-967 --- .../operation/preprocess/Preprocessors.java | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java index ba741ff1..6a36ed81 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java @@ -68,44 +68,6 @@ public static OperationPreprocessor prettyPrint() { return new ContentModifyingOperationPreprocessor(new PrettyPrintingContentModifier()); } - /** - * Returns an {@code OperationPreprocessor} that will remove any header from the - * request or response with a name that is equal to one of the given - * {@code headersToRemove}. - * @param headerNames the header names - * @return the preprocessor - * @deprecated since 3.0.0 in favor of {@link #modifyHeaders()} and - * {@link HeadersModifyingOperationPreprocessor#remove(String)} - * @see String#equals(Object) - */ - @Deprecated - public static OperationPreprocessor removeHeaders(String... headerNames) { - HeadersModifyingOperationPreprocessor preprocessor = new HeadersModifyingOperationPreprocessor(); - for (String headerName : headerNames) { - preprocessor.remove(headerName); - } - return preprocessor; - } - - /** - * Returns an {@code OperationPreprocessor} that will remove any headers from the - * request or response with a name that matches one of the given - * {@code headerNamePatterns} regular expressions. - * @param headerNamePatterns the header name patterns - * @return the preprocessor - * @deprecated since 3.0.0 in favor of {@link #modifyHeaders()} and - * {@link HeadersModifyingOperationPreprocessor#removeMatching(String)} - * @see java.util.regex.Matcher#matches() - */ - @Deprecated - public static OperationPreprocessor removeMatchingHeaders(String... headerNamePatterns) { - HeadersModifyingOperationPreprocessor preprocessor = new HeadersModifyingOperationPreprocessor(); - for (String headerNamePattern : headerNamePatterns) { - preprocessor.removeMatching(headerNamePattern); - } - return preprocessor; - } - /** * Returns an {@code OperationPreprocessor} that will mask the href of hypermedia * links in the request or response. From 7e02c125f197cdd3299a8fa2ff945a73fb71f89c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Jun 2025 07:09:01 +0100 Subject: [PATCH 15/15] Next development version (v3.0.5-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fbd1b332..e945e093 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.0.4-SNAPSHOT +version=3.0.5-SNAPSHOT org.gradle.caching=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8