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.Asciidoctor; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Gerrit Meier * @author Andy Wilkinson */ -public abstract class AbstractOperationBlockMacroTests { - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - private Options options; +abstract class AbstractOperationBlockMacroTests { private final Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - @Before - public void setUp() throws IOException { + @TempDir + protected File temp; + + private Options options; + + @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 abstract class AbstractOperationBlockMacroTests { } @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 abstract class AbstractOperationBlockMacroTests { } @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 abstract class AbstractOperationBlockMacroTests { } @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 abstract class AbstractOperationBlockMacroTests { } @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 abstract class AbstractOperationBlockMacroTests { } @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 abstract class AbstractOperationBlockMacroTests { } @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 abstract class AbstractOperationBlockMacroTests { } @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 java.io.File; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 @@ package org.springframework.restdocs.asciidoctor; 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.File; 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 @@ public class MavenOperationBlockMacroTests extends AbstractOperationBlockMacroTe @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.io.IOException; 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 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; */ 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 abstract class TemplatedSnippet implements Snippet { 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 @@ import org.springframework.web.bind.annotation.RequestMethod; * * @author Andy Wilkinson */ -@RunWith(Parameterized.class) public abstract class AbstractSnippetTests { - protected final TemplateFormat templateFormat; + protected final TemplateFormat templateFormat = TemplateFormats.asciidoctor(); - @Rule - public GeneratedSnippets generatedSnippets; - - @Rule - public OperationBuilder operationBuilder; - - @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.Arrays; 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 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; * @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 class RestDocumentationGeneratorTests { } @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 class RestDocumentationGeneratorTests { } @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 class RestDocumentationGeneratorTests { } @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 class RestDocumentationGeneratorTests { } @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 @@ package org.springframework.restdocs.cli; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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 @@ package org.springframework.restdocs.cli; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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 class CurlRequestSnippetTests extends AbstractSnippetTests { .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 class CurlRequestSnippetTests extends AbstractSnippetTests { .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 @@ package org.springframework.restdocs.cli; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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.HashMap; 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 @@ import static org.mockito.Mockito.mock; * @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 class RestDocumentationConfigurerTests { } @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 class RestDocumentationConfigurerTests { } @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 class RestDocumentationConfigurerTests { } @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 class RestDocumentationConfigurerTests { @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 class RestDocumentationConfigurerTests { } @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 class RestDocumentationConfigurerTests { } @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 class RestDocumentationConfigurerTests { @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 class RestDocumentationConfigurerTests { @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 class RestDocumentationConfigurerTests { } @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 class RestDocumentationConfigurerTests { } @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 @@ package org.springframework.restdocs.constraints; 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 @@ import static org.mockito.Mockito.mock; * * @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 class ConstraintDescriptionsTests { } @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.LuhnCheck; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class ResourceBundleConstraintDescriptionResolverTests { } @Test - public void customResourceBundle() { + void customResourceBundle() { ResourceBundle bundle = new ListResourceBundle() { @Override @@ -294,7 +294,7 @@ public class ResourceBundleConstraintDescriptionResolverTests { } @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.api.Condition; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class ValidatorConstraintResolverTests { } @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.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.cookies.CookieDocumentation.cookieWithName; import static org.springframework.restdocs.snippet.Attributes.attributes; import static org.springframework.restdocs.snippet.Attributes.key; @@ -42,138 +39,141 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Clyde Stubbs * @author Andy Wilkinson */ -public class RequestCookiesSnippetTests extends AbstractSnippetTests { +class RequestCookiesSnippetTests { - public RequestCookiesSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @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]"); + } + + @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.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.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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Clyde Stubbs * @author Andy Wilkinson */ -public class ResponseCookiesSnippetTests extends AbstractSnippetTests { +class ResponseCookiesSnippetTests { - public ResponseCookiesSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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 @@ package org.springframework.restdocs.headers; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Andreas Evers * @author Andy Wilkinson */ -public class RequestHeadersSnippetTests extends AbstractSnippetTests { +class RequestHeadersSnippetTests { - public RequestHeadersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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 class RequestHeadersSnippetTests extends AbstractSnippetTests { .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 class RequestHeadersSnippetTests extends AbstractSnippetTests { .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 @@ package org.springframework.restdocs.headers; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Andreas Evers * @author Andy Wilkinson */ -public class ResponseHeadersSnippetTests extends AbstractSnippetTests { +class ResponseHeadersSnippetTests { - public ResponseHeadersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @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]"); + } + + @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 @@ package org.springframework.restdocs.http; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @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 class HttpRequestSnippetTests extends AbstractSnippetTests { .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 class HttpRequestSnippetTests extends AbstractSnippetTests { .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 @@ package org.springframework.restdocs.http; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Andy Wilkinson * @author Jonathan Pearlin */ -public class HttpResponseSnippetTests extends AbstractSnippetTests { +class HttpResponseSnippetTests { - public HttpResponseSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); + @RenderedSnippetTest + void basicResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { + new HttpResponseSnippet().document(operationBuilder.build()); + assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok()); } - @Test - public void basicResponse() throws IOException { - new HttpResponseSnippet().document(this.operationBuilder.build()); - assertThat(this.generatedSnippets.httpResponse()).is(httpResponse(HttpStatus.OK)); + @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 nonOkResponse() throws IOException { - new HttpResponseSnippet().document(this.operationBuilder.response().status(HttpStatus.BAD_REQUEST).build()); - assertThat(this.generatedSnippets.httpResponse()).is(httpResponse(HttpStatus.BAD_REQUEST)); - } - - @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.io.IOException; 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 @@ import static org.mockito.Mockito.verify; * * @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 class ContentTypeLinkExtractorTests { } @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.Collections; 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 org.springframework.util.MultiValueMap; 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 class LinkExtractorsPayloadTests { } @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 @@ package org.springframework.restdocs.hypermedia; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class LinksSnippetTests extends AbstractSnippetTests { +class LinksSnippetTests { - public LinksSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @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]"); + } + + @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 @@ package org.springframework.restdocs.operation.preprocess; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Andy Wilkinson * */ -public class ContentModifyingOperationPreprocessorTests { +class ContentModifyingOperationPreprocessorTests { private final OperationRequestFactory requestFactory = new OperationRequestFactory(); @@ -56,7 +56,7 @@ public class ContentModifyingOperationPreprocessorTests { }); @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 class ContentModifyingOperationPreprocessorTests { } @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 class ContentModifyingOperationPreprocessorTests { } @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 @@ package org.springframework.restdocs.operation.preprocess; import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.operation.OperationRequest; @@ -31,10 +31,10 @@ import static org.mockito.Mockito.mock; * * @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 @@ package org.springframework.restdocs.operation.preprocess; import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.restdocs.operation.OperationResponse; @@ -31,10 +31,10 @@ import static org.mockito.Mockito.mock; * * @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.Collections; 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 @@ import static org.assertj.core.api.Assertions.entry; * @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 class HeadersModifyingOperationPreprocessorTests { } @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 class HeadersModifyingOperationPreprocessorTests { } @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 class HeadersModifyingOperationPreprocessorTests { } @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 class HeadersModifyingOperationPreprocessorTests { } @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 class HeadersModifyingOperationPreprocessorTests { } @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 class HeadersModifyingOperationPreprocessorTests { } @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 class HeadersModifyingOperationPreprocessorTests { } @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.annotation.JsonProperty; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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.Charset; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class PatternReplacingContentModifierTests { } @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 class PatternReplacingContentModifierTests { } @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 class PatternReplacingContentModifierTests { } @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.HashMap; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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 class PrettyPrintingContentModifierTests { } @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.Arrays; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 class UriModifyingOperationPreprocessorTests { } @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWit * * @author Andy Wilkinson */ -public class AsciidoctorRequestFieldsSnippetTests { +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"); + @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.core.JsonProcessingException; 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 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * * @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 class FieldPathPayloadSubsectionExtractorTests { @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 class FieldPathPayloadSubsectionExtractorTests { @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 class FieldPathPayloadSubsectionExtractorTests { @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 class FieldPathPayloadSubsectionExtractorTests { @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 class FieldPathPayloadSubsectionExtractorTests { } @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 class FieldPathPayloadSubsectionExtractorTests { } @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 class FieldPathPayloadSubsectionExtractorTests { } @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 class FieldPathPayloadSubsectionExtractorTests { @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 class FieldPathPayloadSubsectionExtractorTests { @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 class FieldPathPayloadSubsectionExtractorTests { } @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 class FieldPathPayloadSubsectionExtractorTests { } @Test - public void extractedSubsectionIsNotPrettyPrintedWhenInputIsNotPrettyPrinted() + void extractedSubsectionIsNotPrettyPrintedWhenInputIsNotPrettyPrinted() throws JsonParseException, JsonMappingException, JsonProcessingException, IOException { ObjectMapper objectMapper = new ObjectMapper(); byte[] payload = objectMapper @@ -169,7 +167,7 @@ public class FieldPathPayloadSubsectionExtractorTests { } @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 class FieldPathPayloadSubsectionExtractorTests { } @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 @@ package org.springframework.restdocs.payload; 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.Arrays; 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 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 class JsonContentHandlerTests { } @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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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.io.IOException; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class JsonFieldPathsTests { } @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 class JsonFieldPathsTests { } @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 class JsonFieldPathsTests { } @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.List; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class JsonFieldProcessorTests { } @Test - public void extractTopLevelArray() { + void extractTopLevelArray() { List> payload = new ArrayList<>(); Map bravo = new HashMap<>(); bravo.put("b", "bravo"); @@ -68,7 +68,7 @@ public class JsonFieldProcessorTests { } @Test - public void extractArray() { + void extractArray() { Map payload = new HashMap<>(); Map bravo = new HashMap<>(); bravo.put("b", "bravo"); @@ -78,7 +78,7 @@ public class JsonFieldProcessorTests { } @Test - public void extractArrayContents() { + void extractArrayContents() { Map payload = new HashMap<>(); Map bravo = new HashMap<>(); bravo.put("b", "bravo"); @@ -88,7 +88,7 @@ public class JsonFieldProcessorTests { } @Test - public void extractFromItemsInArray() { + void extractFromItemsInArray() { Map payload = new HashMap<>(); Map entry = new HashMap<>(); entry.put("b", "bravo"); @@ -98,7 +98,7 @@ public class JsonFieldProcessorTests { } @Test - public void extractOccasionallyAbsentFieldFromItemsInArray() { + void extractOccasionallyAbsentFieldFromItemsInArray() { Map payload = new HashMap<>(); Map entry = new HashMap<>(); entry.put("b", "bravo"); @@ -109,7 +109,7 @@ public class JsonFieldProcessorTests { } @Test - public void extractOccasionallyNullFieldFromItemsInArray() { + void extractOccasionallyNullFieldFromItemsInArray() { Map payload = new HashMap<>(); Map nonNullField = new HashMap<>(); nonNullField.put("b", "bravo"); @@ -121,7 +121,7 @@ public class JsonFieldProcessorTests { } @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 class JsonFieldProcessorTests { } @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 class JsonFieldProcessorTests { } @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 class JsonFieldProcessorTests { } @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 class JsonFieldProcessorTests { } @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 class JsonFieldProcessorTests { } @Test - public void removeTopLevelMapEntry() { + void removeTopLevelMapEntry() { Map payload = new HashMap<>(); payload.put("a", "alpha"); this.fieldProcessor.remove("a", payload); @@ -216,7 +216,7 @@ public class JsonFieldProcessorTests { } @Test - public void mapWithEntriesIsNotRemovedWhenNotAlsoRemovingDescendants() { + void mapWithEntriesIsNotRemovedWhenNotAlsoRemovingDescendants() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -226,7 +226,7 @@ public class JsonFieldProcessorTests { } @Test - public void removeSubsectionRemovesMapWithEntries() { + void removeSubsectionRemovesMapWithEntries() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -236,7 +236,7 @@ public class JsonFieldProcessorTests { } @Test - public void removeNestedMapEntry() { + void removeNestedMapEntry() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -247,7 +247,7 @@ public class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { @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 class JsonFieldProcessorTests { } @Test - public void removeUsingLeafWildcard() { + void removeUsingLeafWildcard() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -379,7 +379,7 @@ public class JsonFieldProcessorTests { } @Test - public void removeUsingTopLevelWildcard() { + void removeUsingTopLevelWildcard() { Map payload = new HashMap<>(); Map alpha = new HashMap<>(); payload.put("a", alpha); @@ -390,7 +390,7 @@ public class JsonFieldProcessorTests { } @Test - public void removeUsingMidLevelWildcard() { + void removeUsingMidLevelWildcard() { Map payload = new LinkedHashMap<>(); Map alpha = new LinkedHashMap<>(); payload.put("a", alpha); @@ -407,28 +407,28 @@ public class JsonFieldProcessorTests { } @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 class JsonFieldProcessorTests { } @Test - public void hasFieldIsTrueForAlwaysNullFieldBeneathArray() { + void hasFieldIsTrueForAlwaysNullFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", null); @@ -446,7 +446,7 @@ public class JsonFieldProcessorTests { } @Test - public void hasFieldIsFalseForAlwaysAbsentFieldBeneathArray() { + void hasFieldIsFalseForAlwaysAbsentFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", "bravo"); @@ -455,7 +455,7 @@ public class JsonFieldProcessorTests { } @Test - public void hasFieldIsFalseForOccasionallyAbsentFieldBeneathArray() { + void hasFieldIsFalseForOccasionallyAbsentFieldBeneathArray() { Map payload = new HashMap<>(); Map nested = new HashMap<>(); nested.put("b", "bravo"); @@ -464,7 +464,7 @@ public class JsonFieldProcessorTests { } @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * * @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @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 class PayloadDocumentationTests { } @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 class PayloadDocumentationTests { } @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 class PayloadDocumentationTests { } @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class RequestBodyPartSnippetTests extends AbstractSnippetTests { +class RequestBodyPartSnippetTests { - public RequestBodyPartSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class RequestBodySnippetTests extends AbstractSnippetTests { +class RequestBodySnippetTests { - public RequestBodySnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); + @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 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 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 requestWithNoBody() throws IOException { - requestBody().document(this.operationBuilder.request("http://localhost").build()); - assertThat(this.generatedSnippets.snippet("request-body")).is(codeBlock(null, "nowrap").withContent("")); - } - - @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Andy Wilkinson * @author Sungjun Lee */ -public class RequestFieldsSnippetTests extends AbstractSnippetTests { +class RequestFieldsSnippetTests { - public RequestFieldsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); - } - - @Test - public void missingOptionalRequestField() 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")); - } - - @Test - public void missingIgnoredOptionalRequestFieldDoesNotRequireAType() 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")); - } - - @Test - public void presentOptionalRequestField() 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")); + 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 requestFieldsWithCustomAttributes() throws IOException { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource("request-fields")) - .willReturn(snippetResource("request-fields-with-title")); + @RenderedSnippetTest + void missingOptionalRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) + .document(operationBuilder.request("http://localhost").content("{}").build()); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); + } + + @RenderedSnippetTest + void missingIgnoredOptionalRequestFieldDoesNotRequireAType(OperationBuilder operationBuilder, + AssertableSnippets snippets) throws IOException { + new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").ignored().optional())) + .document(operationBuilder.request("http://localhost").content("{}").build()); + assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description")); + } + + @RenderedSnippetTest + void presentOptionalRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) + throws IOException { + new RequestFieldsSnippet( + Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) + .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")); + } + + @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")); + assertThat(snippets.requestFields()) + .isTable((table) -> table.withHeader("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("|", "\\|"); + @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.io.IOException; 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 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWit * @author Mathieu Pousse * @author Andy Wilkinson */ -public class RequestPartFieldsSnippetTests extends AbstractSnippetTests { +public class RequestPartFieldsSnippetTests { - public RequestPartFieldsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @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 @@ package org.springframework.restdocs.payload; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * @author Andy Wilkinson * @author Sungjun Lee */ -public class ResponseFieldsSnippetTests extends AbstractSnippetTests { +public class ResponseFieldsSnippetTests { - public ResponseFieldsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); + 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 prefixedAdditionalDescriptors() throws IOException { + @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")); + assertThat(snippets.responseFields()) + .isTable((table) -> table.withHeader("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("|", "\\|"); + @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.Arrays; 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 @@ package org.springframework.restdocs.request; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class FormParametersSnippetTests extends AbstractSnippetTests { +class FormParametersSnippetTests { - public FormParametersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @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]"); + } + + @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 @@ package org.springframework.restdocs.request; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class PathParametersSnippetTests extends AbstractSnippetTests { +class PathParametersSnippetTests { - public PathParametersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); - } - - @Test - public void ignoredPathParameter() 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")); - } - - @Test - public void allUndocumentedPathParametersCanBeIgnored() 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")); - } - - @Test - public void missingOptionalPathParameter() 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}") + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle("/{a}"), "Parameter", "Description").row("`a`", "one") + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "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}") + @RenderedSnippetTest + void ignoredPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { + new PathParametersSnippet( + Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) + .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") .build()); - assertThat(this.generatedSnippets.pathParameters()) - .is(tableWithTitleAndHeader(getTitle("/{a}"), "Parameter", "Description").row("`a`", "one")); + assertThat(snippets.pathParameters()) + .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") + .row("`b`", "two")); } - @Test - public void pathParametersWithQueryString() throws IOException { + @RenderedSnippetTest + void allUndocumentedPathParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets, + TemplateFormat templateFormat) throws IOException { + new PathParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true).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")); + } + + @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( + 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")); + } + + @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")); + } + + @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]"); } - private String getTitle() { - return getTitle("/{a}/{b}"); + @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]"); } - private String getTitle(String title) { - if (this.templateFormat.getId().equals(TemplateFormats.asciidoctor().getId())) { + @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(TemplateFormat templateFormat) { + return getTitle(templateFormat, "/{a}/{b}"); + } + + 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 @@ package org.springframework.restdocs.request; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class QueryParametersSnippetTests extends AbstractSnippetTests { +class QueryParametersSnippetTests { - public QueryParametersSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @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]"); + } + + @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 @@ package org.springframework.restdocs.request; 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 @@ import static org.springframework.restdocs.snippet.Attributes.key; * * @author Andy Wilkinson */ -public class RequestPartsSnippetTests extends AbstractSnippetTests { +class RequestPartsSnippetTests { - public RequestPartsSnippetTests(String name, TemplateFormat templateFormat) { - super(name, templateFormat); - } - - @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")); } - private String escapeIfNecessary(String input) { - if (this.templateFormat.getId().equals(TemplateFormats.markdown().getId())) { - return input; - } - return input.replace("|", "\\|"); + @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]"); + } + + @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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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.FileReader; 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 @@ import static org.mockito.Mockito.mock; * * @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 class StandardWriterResolverTests { } @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 class StandardWriterResolverTests { } @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.Collections; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class TemplatedSnippetTests { } @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.HashMap; 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 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; * * @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 class StandardTemplateResourceResolverTests { } @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 class StandardTemplateResourceResolverTests { } @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 class StandardTemplateResourceResolverTests { } @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 @@ package org.springframework.restdocs.templates.mustache; 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 @@ import static org.mockito.Mockito.mock; * * @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 class AsciidoctorTableCellContentLambdaTests { } @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.List; 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 @@ import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; * * @author Andy Wilkinson */ -public class OperationBuilder extends OperationTestRule { +public class OperationBuilder { private final Map attributes = new HashMap<>(); - private OperationResponseBuilder responseBuilder; + private final File outputDirectory; - private String name; - - 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 class OperationBuilder extends OperationTestRule { 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 @@ public class OperationBuilder extends OperationTestRule { 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 @@ import org.springframework.util.Assert; * @author Madhura Bhave * @author Phillip Webb * @author Andy Wilkinson - * @author Sam Brannen - * @see OutputCaptureRule */ class OutputCapture implements CapturedOutput { @@ -61,7 +59,7 @@ class OutputCapture implements CapturedOutput { 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 @@ class OutputCapture implements CapturedOutput { } /** - * 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 @@ class OutputCapture implements CapturedOutput { } 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.Arrays; 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 @@ import static org.mockito.Mockito.mock; * * @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 class MockMvcRequestConverterTests { } @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 java.util.Collections; 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 @@ import static org.assertj.core.api.Assertions.entry; * * @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 class MockMvcResponseConverterTests { } @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 class MockMvcResponseConverterTests { } @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 @@ package org.springframework.restdocs.mockmvc; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * @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 class MockMvcRestDocumentationConfigurerTests { } @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 class MockMvcRestDocumentationConfigurerTests { } @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 class MockMvcRestDocumentationConfigurerTests { } @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 class MockMvcRestDocumentationConfigurerTests { @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 class MockMvcRestDocumentationConfigurerTests { @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 java.util.regex.Pattern; 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.HttpHeaders; 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.CodeBlockCond 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 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 class MockMvcRestDocumentationIntegrationTests { } @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 @@ package org.springframework.restdocs.mockmvc; 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 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuild * @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 @@ package org.springframework.restdocs.restassured; 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 @@ import static org.assertj.core.api.Assertions.assertThat; * * @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 class RestAssuredParameterBehaviorTests { } @Test - public void queryParameterOnHead() { + void queryParameterOnHead() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .head("/query-parameter") @@ -74,7 +74,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void queryParameterOnPost() { + void queryParameterOnPost() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .post("/query-parameter") @@ -84,7 +84,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void queryParameterOnPut() { + void queryParameterOnPut() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .put("/query-parameter") @@ -94,7 +94,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void queryParameterOnPatch() { + void queryParameterOnPatch() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .patch("/query-parameter") @@ -104,7 +104,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void queryParameterOnDelete() { + void queryParameterOnDelete() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .delete("/query-parameter") @@ -114,7 +114,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void queryParameterOnOptions() { + void queryParameterOnOptions() { this.spec.queryParam("a", "alpha", "apple") .queryParam("b", "bravo") .options("/query-parameter") @@ -124,49 +124,49 @@ public class RestAssuredParameterBehaviorTests { } @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 class RestAssuredParameterBehaviorTests { } @Test - public void formParamOnHead() { + void formParamOnHead() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .head("/form-url-encoded") @@ -186,7 +186,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void formParamOnPost() { + void formParamOnPost() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .post("/form-url-encoded") @@ -196,7 +196,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void formParamOnPut() { + void formParamOnPut() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .put("/form-url-encoded") @@ -206,7 +206,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void formParamOnPatch() { + void formParamOnPatch() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .patch("/form-url-encoded") @@ -216,7 +216,7 @@ public class RestAssuredParameterBehaviorTests { } @Test - public void formParamOnDelete() { + void formParamOnDelete() { this.spec.formParam("a", "alpha", "apple") .formParam("b", "bravo") .delete("/form-url-encoded") @@ -226,7 +226,7 @@ public class RestAssuredParameterBehaviorTests { } @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 java.util.Iterator; 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 @@ import static org.assertj.core.api.Assertions.entry; * * @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @Test - public void headersWithCustomAccept() { + void headersWithCustomAccept() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .header("Foo", "bar") @@ -117,7 +117,7 @@ public class RestAssuredRequestConverterTests { } @Test - public void cookies() { + void cookies() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .cookie("cookie1", "cookieVal1") @@ -138,7 +138,7 @@ public class RestAssuredRequestConverterTests { } @Test - public void multipart() { + void multipart() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart("a", "a.txt", "alpha", null) @@ -156,14 +156,14 @@ public class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @Test - public void multipartWithByteArrayBody() { + void multipartWithByteArrayBody() { RequestSpecification requestSpec = RestAssured.given() .port(tomcat.getPort()) .multiPart("control", "file", "foo".getBytes()); @@ -237,7 +237,7 @@ public class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 class RestAssuredRequestConverterTests { } @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 @@ package org.springframework.restdocs.restassured; 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 @@ import static org.mockito.Mockito.mock; * * @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 java.util.Map; 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 @@ import static org.mockito.Mockito.verify; * @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 java.util.regex.Pattern; 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 @@ import static org.springframework.restdocs.restassured.RestAssuredRestDocumentat * @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { 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 class RestAssuredRestDocumentationIntegrationTests { } @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 class RestAssuredRestDocumentationIntegrationTests { } @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 jakarta.servlet.http.HttpServletResponse; 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.net.URI; 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 @@ import static org.springframework.web.reactive.function.server.RequestPredicates * * @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @Test - public void requestWithHeaders() { + void requestWithHeaders() { ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/"), (req) -> null)) .configureClient() .baseUrl("http://localhost") @@ -105,7 +105,7 @@ public class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @Test - public void postRequestWithNoContentType() { + void postRequestWithNoContentType() { ExchangeResult result = WebTestClient .bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> ServerResponse.ok().build())) .configureClient() @@ -244,7 +244,7 @@ public class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 class WebTestClientRequestConverterTests { } @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 @@ package org.springframework.restdocs.webtestclient; 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 @@ import static org.springframework.web.reactive.function.server.RequestPredicates * * @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 class WebTestClientResponseConverterTests { } @Test - public void responseWithCookie() { + void responseWithCookie() { ExchangeResult result = WebTestClient .bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> ServerResponse.ok() @@ -92,7 +92,7 @@ public class WebTestClientResponseConverterTests { } @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 @@ package org.springframework.restdocs.webtestclient; 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 @@ import static org.mockito.Mockito.verify; * * @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 class WebTestClientRestDocumentationConfigurerTests { } @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 class WebTestClientRestDocumentationConfigurerTests { } @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.Collectors; 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 @@ import static org.springframework.web.reactive.function.BodyInserters.fromValue; * * @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 class WebTestClientRestDocumentationIntegrationTests { 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 class WebTestClientRestDocumentationIntegrationTests { } @Test - public void pathParametersSnippet() { + void pathParametersSnippet() { this.webTestClient.get() .uri("/{foo}/{bar}", "1", "2") .exchange() @@ -136,7 +135,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void queryParametersSnippet() { + void queryParametersSnippet() { this.webTestClient.get() .uri("/?a=alpha&b=bravo") .exchange() @@ -153,7 +152,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void multipart() { + void multipart() { MultiValueMap multipartData = new LinkedMultiValueMap<>(); multipartData.add("a", "alpha"); multipartData.add("b", "bravo"); @@ -173,7 +172,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void responseWithSetCookie() { + void responseWithSetCookie() { this.webTestClient.get() .uri("/set-cookie") .exchange() @@ -187,7 +186,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void curlSnippetWithCookies() { + void curlSnippetWithCookies() { this.webTestClient.get() .uri("/") .cookie("cookieName", "cookieVal") @@ -204,7 +203,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void curlSnippetWithEmptyParameterQueryString() { + void curlSnippetWithEmptyParameterQueryString() { this.webTestClient.get() .uri("/?a=") .accept(MediaType.APPLICATION_JSON) @@ -220,7 +219,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void httpieSnippetWithCookies() { + void httpieSnippetWithCookies() { this.webTestClient.get() .uri("/") .cookie("cookieName", "cookieVal") @@ -237,7 +236,7 @@ public class WebTestClientRestDocumentationIntegrationTests { } @Test - public void illegalStateExceptionShouldBeThrownWhenCallDocumentWebClientNotConfigured() { + void illegalStateExceptionShouldBeThrownWhenCallDocumentWebClientNotConfigured() { assertThatThrownBy(() -> this.webTestClient .mutateWith((builder, httpHandlerBuilder, connector) -> builder.filters(List::clear).build()) .get()