Allow URL to be specified when working with HtmlUnit beans

Update `@AutoConfigureMockMvc` to move HtmlUnit configuration under
a dedicated attribute and provide support for configuring the URL
that is used.

Closes gh-47857
This commit is contained in:
Phillip Webb
2025-10-29 20:47:42 -07:00
parent 30a6b50e82
commit 2e79fc01cb
8 changed files with 92 additions and 39 deletions
@@ -72,19 +72,43 @@ public @interface AutoConfigureMockMvc {
boolean printOnlyOnFailure() default true;
/**
* If a {@link WebClient} should be auto-configured when HtmlUnit is on the classpath.
* Defaults to {@code true}.
* @return if a {@link WebClient} is auto-configured
* Settings for the integration between MockMVC and HtmlUnit.
* @return the HtmlUnit settings
*/
@PropertyMapping("webclient.enabled")
boolean webClientEnabled() default true;
@PropertyMapping("htmlunit")
HtmlUnit htmlUnit() default @HtmlUnit;
/**
* If a {@link WebDriver} should be auto-configured when Selenium is on the classpath.
* Defaults to {@code true}.
* @return if a {@link WebDriver} is auto-configured
* HtmlUnit settings.
*/
@PropertyMapping("webdriver.enabled")
boolean webDriverEnabled() default true;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface HtmlUnit {
/**
* The URL that should be used when expanding relative paths.
* @return the URL used to expand relative paths
*/
String url() default "http://localhost";
/**
* If a {@link WebClient} should be auto-configured when HtmlUnit is on the
* classpath. Defaults to {@code true}.
* @return if a {@link WebClient} is auto-configured
*/
@PropertyMapping("webclient.enabled")
boolean webClient() default true;
/**
* If a {@link WebDriver} should be auto-configured when Selenium is on the
* classpath. Defaults to {@code true}.
* @return if a {@link WebDriver} is auto-configured
*/
@PropertyMapping("webdriver.enabled")
boolean webDriver() default true;
}
}
@@ -25,10 +25,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.web.htmlunit.UriBuilderFactoryWebClient;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
/**
* Auto-configuration for HtmlUnit {@link WebClient} MockMVC integration.
@@ -38,21 +38,16 @@ import org.springframework.web.util.UriBuilderFactory;
*/
@AutoConfiguration(after = MockMvcAutoConfiguration.class)
@ConditionalOnClass(WebClient.class)
@ConditionalOnBooleanProperty(name = "spring.test.mockmvc.webclient.enabled", matchIfMissing = true)
@ConditionalOnBooleanProperty(name = "spring.test.mockmvc.htmlunit.webclient.enabled", matchIfMissing = true)
public final class MockMvcWebClientAutoConfiguration {
/**
* A {@link UriBuilderFactory} that is suitable for Mock access (i.e. without a
* running web server).
*/
private static final UriBuilderFactory MOCK_URI_BUILDER_FACTORY = new DefaultUriBuilderFactory("http://localhost");
@Bean
@ConditionalOnMissingBean({ WebClient.class, MockMvcWebClientBuilder.class })
@ConditionalOnBean(MockMvc.class)
MockMvcWebClientBuilder mockMvcWebClientBuilder(MockMvc mockMvc) {
MockMvcWebClientBuilder mockMvcWebClientBuilder(Environment environment, MockMvc mockMvc) {
String url = environment.getProperty("spring.test.mockmvc.htmlunit.url", "http://localhost");
return MockMvcWebClientBuilder.mockMvcSetup(mockMvc)
.withDelegate(new UriBuilderFactoryWebClient(MOCK_URI_BUILDER_FACTORY));
.withDelegate(new UriBuilderFactoryWebClient(new DefaultUriBuilderFactory(url)));
}
@Bean
@@ -28,10 +28,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.web.htmlunit.UriBuilderFactoryWebConnectionHtmlUnitDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
/**
* Auto-configuration for Selenium {@link WebDriver} MockMVC integration.
@@ -41,22 +41,17 @@ import org.springframework.web.util.UriBuilderFactory;
*/
@AutoConfiguration(after = MockMvcAutoConfiguration.class)
@ConditionalOnClass(HtmlUnitDriver.class)
@ConditionalOnBooleanProperty(name = "spring.test.mockmvc.webdriver.enabled", matchIfMissing = true)
@ConditionalOnBooleanProperty(name = "spring.test.mockmvc.htmlunit.webdriver.enabled", matchIfMissing = true)
public final class MockMvcWebDriverAutoConfiguration {
/**
* A {@link UriBuilderFactory} that is suitable for Mock access (i.e. without a
* running web server).
*/
private static final UriBuilderFactory MOCK_URI_BUILDER_FACTORY = new DefaultUriBuilderFactory("http://localhost");
@Bean
@ConditionalOnMissingBean({ WebDriver.class, MockMvcHtmlUnitDriverBuilder.class })
@ConditionalOnBean(MockMvc.class)
MockMvcHtmlUnitDriverBuilder mockMvcHtmlUnitDriverBuilder(MockMvc mockMvc) {
MockMvcHtmlUnitDriverBuilder mockMvcHtmlUnitDriverBuilder(Environment environment, MockMvc mockMvc) {
String url = environment.getProperty("spring.test.mockmvc.htmlunit.url", "http://localhost");
return MockMvcHtmlUnitDriverBuilder.mockMvcSetup(mockMvc)
.withDelegate(
new UriBuilderFactoryWebConnectionHtmlUnitDriver(MOCK_URI_BUILDER_FACTORY, BrowserVersion.CHROME));
.withDelegate(new UriBuilderFactoryWebConnectionHtmlUnitDriver(new DefaultUriBuilderFactory(url),
BrowserVersion.CHROME));
}
@Bean
@@ -1,16 +1,46 @@
{
"properties": [
{
"name": "spring.test.mockmvc.webclient.enabled",
"name": "spring.test.mockmvc.htmlunit.url",
"type": "java.lang.Boolean",
"description": "URL to use when HtmlUnit expands relative paths.",
"defaultValue": "http://localhost"
},
{
"name": "spring.test.mockmvc.htmlunit.webclient.enabled",
"type": "java.lang.Boolean",
"description": "Whether HTMLUnit's WebClient should be auto-configured when it's on the classpath.",
"defaultValue": true
},
{
"name": "spring.test.mockmvc.webdriver.enabled",
"name": "spring.test.mockmvc.htmlunit.webdriver.enabled",
"type": "java.lang.Boolean",
"description": "Whether Selenium's WebDriver should be auto-configured when it's on the classpath.",
"defaultValue": true
},
{
"name": "spring.test.mockmvc.webclient.enabled",
"type": "java.lang.Boolean",
"description": "Whether HTMLUnit's WebClient should be auto-configured when it's on the classpath.",
"defaultValue": true,
"deprecated": true,
"deprecation": {
"level": "error",
"replacement": "spring.test.mockmvc.htmlunit.webclient.enabled",
"since": "4.0.0"
}
},
{
"name": "spring.test.mockmvc.webdriver.enabled",
"type": "java.lang.Boolean",
"description": "Whether Selenium's WebDriver should be auto-configured when it's on the classpath.",
"defaultValue": true,
"deprecated": true,
"deprecation": {
"level": "error",
"replacement": "spring.test.mockmvc.htmlunit.webdriver.enabled",
"since": "4.0.0"
}
}
]
}
@@ -21,6 +21,8 @@ import org.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc.HtmlUnit;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -31,7 +33,8 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Phillip Webb
*/
@WebMvcTest
class WebMvcTestWebClientIntegrationTests {
@AutoConfigureMockMvc(htmlUnit = @HtmlUnit(url = "http://localhost:8181"))
class WebMvcTestHtmlUnitWebClientIntegrationTests {
@Autowired
private WebClient webClient;
@@ -40,6 +43,7 @@ class WebMvcTestWebClientIntegrationTests {
void shouldAutoConfigureWebClient() throws Exception {
HtmlPage page = this.webClient.getPage("/html");
assertThat(page.getBody().getTextContent()).isEqualTo("Hello");
assertThat(page.getBaseURI()).isEqualTo("http://localhost:8181/html");
}
}
@@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@WebMvcTest
@TestMethodOrder(MethodOrderer.MethodName.class)
class WebMvcTestWebDriverCustomScopeIntegrationTests {
class WebMvcTestHtmlUnitWebDriverCustomScopeIntegrationTests {
// gh-7454
@@ -52,7 +52,7 @@ class WebMvcTestWebDriverCustomScopeIntegrationTests {
@Test
void shouldAutoConfigureWebClient() {
WebMvcTestWebDriverCustomScopeIntegrationTests.previousWebDriver = this.webDriver;
WebMvcTestHtmlUnitWebDriverCustomScopeIntegrationTests.previousWebDriver = this.webDriver;
}
@Test
@@ -25,6 +25,8 @@ import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc.HtmlUnit;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -37,7 +39,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*/
@WebMvcTest
@TestMethodOrder(MethodOrderer.MethodName.class)
class WebMvcTestWebDriverIntegrationTests {
@AutoConfigureMockMvc(htmlUnit = @HtmlUnit(url = "http://localhost:8181"))
class WebMvcTestHtmlUnitWebDriverIntegrationTests {
private static WebDriver previousWebDriver;
@@ -49,7 +52,7 @@ class WebMvcTestWebDriverIntegrationTests {
this.webDriver.get("/html");
WebElement element = this.webDriver.findElement(By.tagName("body"));
assertThat(element.getText()).isEqualTo("Hello");
WebMvcTestWebDriverIntegrationTests.previousWebDriver = this.webDriver;
WebMvcTestHtmlUnitWebDriverIntegrationTests.previousWebDriver = this.webDriver;
}
@Test
@@ -59,6 +62,7 @@ class WebMvcTestWebDriverIntegrationTests {
assertThat(element.getText()).isEqualTo("Hello");
assertThatExceptionOfType(NoSuchSessionException.class).isThrownBy(previousWebDriver::getWindowHandle);
assertThat(previousWebDriver).isNotNull().isNotSameAs(this.webDriver);
assertThat(this.webDriver.getCurrentUrl()).isEqualTo("http://localhost:8181/html");
}
}
@@ -23,6 +23,7 @@ import org.openqa.selenium.WebDriver;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc.HtmlUnit;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
@@ -38,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Stephane Nicoll
*/
@WebMvcTest
@AutoConfigureMockMvc(addFilters = false, webClientEnabled = false, webDriverEnabled = false)
@AutoConfigureMockMvc(addFilters = false, htmlUnit = @HtmlUnit(webClient = false, webDriver = false))
class WebMvcTestWithAutoConfigureMockMvcIntegrationTests {
@Autowired