From 99779b9f07f83a96d94de7d9ab077033166f494a Mon Sep 17 00:00:00 2001 From: Jean Bisutti Date: Tue, 20 Oct 2026 11:56:36 +0200 Subject: [PATCH] Unwrap the exceptions thrown by a DataSource bean proxied by QuickPerf --- ...ingBoot1DataSourceExceptionJunit4Test.java | 44 ++++++++++ .../SqlExceptionOfDataSource.java | 83 +++++++++++++++++++ ...ingBoot2DataSourceExceptionJunit4Test.java | 44 ++++++++++ .../SqlExceptionOfDataSource.java | 83 +++++++++++++++++++ .../sql/QuickPerfProxyBeanPostProcessor.java | 9 +- .../Spring3DataSourceExceptionJunit4Test.java | 48 +++++++++++ .../SqlExceptionOfDataSource.java | 72 ++++++++++++++++ ...ingBoot3DataSourceExceptionJunit5Test.java | 46 ++++++++++ .../SqlExceptionOfDataSource.java | 82 ++++++++++++++++++ .../sql/QuickPerfProxyBeanPostProcessor.java | 9 +- .../sql/QuickPerfProxyBeanPostProcessor.java | 9 +- 11 files changed, 526 insertions(+), 3 deletions(-) create mode 100644 spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot1DataSourceExceptionJunit4Test.java create mode 100644 spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java create mode 100644 spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot2DataSourceExceptionJunit4Test.java create mode 100644 spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java create mode 100644 spring/junit4-spring3/src/test/java/org/quickperf/spring/Spring3DataSourceExceptionJunit4Test.java create mode 100644 spring/junit4-spring3/src/test/java/org/quickperf/spring/datasourceexception/SqlExceptionOfDataSource.java create mode 100644 spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot3DataSourceExceptionJunit5Test.java create mode 100644 spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java diff --git a/spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot1DataSourceExceptionJunit4Test.java b/spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot1DataSourceExceptionJunit4Test.java new file mode 100644 index 00000000..191a6ab3 --- /dev/null +++ b/spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot1DataSourceExceptionJunit4Test.java @@ -0,0 +1,44 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.springboottest; + +import org.junit.Test; +import org.junit.experimental.results.PrintableResult; +import org.quickperf.spring.springboottest.datasourceexception.SqlExceptionOfDataSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SpringBoot1DataSourceExceptionJunit4Test { + + @Test + public void should_fail_with_the_sql_exception_of_a_datasource_bean_refusing_the_connection() { + + // GIVEN + Class testClass = SqlExceptionOfDataSource.class; + + // WHEN + PrintableResult printableResult = PrintableResult.testResult(testClass); + + // THEN + assertThat(printableResult.failureCount()).isOne(); + + String testReport = printableResult.toString(); + // The SQLException starts a line: it is the exception of the failure, not the cause of another one. + assertThat(testReport) + .contains(System.lineSeparator() + "java.sql.SQLException: Connection refused") + .doesNotContain("UndeclaredThrowableException") + .doesNotContain("InvocationTargetException"); + + } + +} diff --git a/spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java b/spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java new file mode 100644 index 00000000..553e8c9d --- /dev/null +++ b/spring/junit4-spring-boot-1-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java @@ -0,0 +1,83 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.springboottest.datasourceexception; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.quickperf.spring.junit4.QuickPerfSpringRunner; +import org.quickperf.spring.springboottest.FootballApplication; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.AbstractDataSource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Verifies that a test getting a connection from a DataSource bean that refuses it fails + * with the SQLException thrown by the DataSource. + */ +@RunWith(QuickPerfSpringRunner.class) +@SpringBootTest(classes = {FootballApplication.class}) +@Import(SqlExceptionOfDataSource.Config.class) +public class SqlExceptionOfDataSource { + + @Autowired + @Qualifier("dataSourceRefusingConnections") + private DataSource dataSourceRefusingConnections; + + @Test + public void get_and_close_a_connection() throws SQLException { + dataSourceRefusingConnections.getConnection().close(); + } + + static class DataSourceRefusingConnections extends AbstractDataSource { + + @Override + public Connection getConnection() throws SQLException { + throw new SQLException("Connection refused"); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + throw new SQLException("Connection refused"); + } + } + + static class Config { + + // Declaring a DataSource bean disables the one auto-configured by Spring Boot. This working + // database replaces it: JPA gets a connection from the primary DataSource at startup. + @Bean + @Primary + DataSource dataSource() { + return new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.HSQL) + .generateUniqueName(true) + .build(); + } + + // The DataSource of the test method, injected with @Qualifier because the other one is primary. + @Bean + DataSource dataSourceRefusingConnections() { + return new DataSourceRefusingConnections(); + } + } +} diff --git a/spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot2DataSourceExceptionJunit4Test.java b/spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot2DataSourceExceptionJunit4Test.java new file mode 100644 index 00000000..8c5b2ee0 --- /dev/null +++ b/spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot2DataSourceExceptionJunit4Test.java @@ -0,0 +1,44 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.springboottest; + +import org.junit.Test; +import org.junit.experimental.results.PrintableResult; +import org.quickperf.spring.springboottest.datasourceexception.SqlExceptionOfDataSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SpringBoot2DataSourceExceptionJunit4Test { + + @Test + public void should_fail_with_the_sql_exception_of_a_datasource_bean_refusing_the_connection() { + + // GIVEN + Class testClass = SqlExceptionOfDataSource.class; + + // WHEN + PrintableResult printableResult = PrintableResult.testResult(testClass); + + // THEN + assertThat(printableResult.failureCount()).isOne(); + + String testReport = printableResult.toString(); + // The SQLException starts a line: it is the exception of the failure, not the cause of another one. + assertThat(testReport) + .contains(System.lineSeparator() + "java.sql.SQLException: Connection refused") + .doesNotContain("UndeclaredThrowableException") + .doesNotContain("InvocationTargetException"); + + } + +} diff --git a/spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java b/spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java new file mode 100644 index 00000000..553e8c9d --- /dev/null +++ b/spring/junit4-spring-boot-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java @@ -0,0 +1,83 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.springboottest.datasourceexception; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.quickperf.spring.junit4.QuickPerfSpringRunner; +import org.quickperf.spring.springboottest.FootballApplication; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.AbstractDataSource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Verifies that a test getting a connection from a DataSource bean that refuses it fails + * with the SQLException thrown by the DataSource. + */ +@RunWith(QuickPerfSpringRunner.class) +@SpringBootTest(classes = {FootballApplication.class}) +@Import(SqlExceptionOfDataSource.Config.class) +public class SqlExceptionOfDataSource { + + @Autowired + @Qualifier("dataSourceRefusingConnections") + private DataSource dataSourceRefusingConnections; + + @Test + public void get_and_close_a_connection() throws SQLException { + dataSourceRefusingConnections.getConnection().close(); + } + + static class DataSourceRefusingConnections extends AbstractDataSource { + + @Override + public Connection getConnection() throws SQLException { + throw new SQLException("Connection refused"); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + throw new SQLException("Connection refused"); + } + } + + static class Config { + + // Declaring a DataSource bean disables the one auto-configured by Spring Boot. This working + // database replaces it: JPA gets a connection from the primary DataSource at startup. + @Bean + @Primary + DataSource dataSource() { + return new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.HSQL) + .generateUniqueName(true) + .build(); + } + + // The DataSource of the test method, injected with @Qualifier because the other one is primary. + @Bean + DataSource dataSourceRefusingConnections() { + return new DataSourceRefusingConnections(); + } + } +} diff --git a/spring/junit4-spring3/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java b/spring/junit4-spring3/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java index 25aef5f7..80fdc1ca 100644 --- a/spring/junit4-spring3/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java +++ b/spring/junit4-spring3/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java @@ -22,6 +22,7 @@ import org.springframework.util.ReflectionUtils; import javax.sql.DataSource; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; // Inspiration from https://blog.arnoldgalovics.com/configuring-a-datasource-proxy-in-spring-boot/ @@ -78,7 +79,13 @@ public Object invoke(final MethodInvocation invocation) throws Throwable { Method proxyMethod = ReflectionUtils.findMethod( this.datasourceProxy.getClass() , invocation.getMethod().getName()); if (proxyMethod != null) { - return proxyMethod.invoke(this.datasourceProxy, invocation.getArguments()); + // Unwrapped so that the caller gets what the DataSource threw, such as an SQLException. + try { + return proxyMethod.invoke(this.datasourceProxy, invocation.getArguments()); + } + catch (InvocationTargetException e) { + throw e.getTargetException(); + } } return invocation.proceed(); } diff --git a/spring/junit4-spring3/src/test/java/org/quickperf/spring/Spring3DataSourceExceptionJunit4Test.java b/spring/junit4-spring3/src/test/java/org/quickperf/spring/Spring3DataSourceExceptionJunit4Test.java new file mode 100644 index 00000000..b17282ec --- /dev/null +++ b/spring/junit4-spring3/src/test/java/org/quickperf/spring/Spring3DataSourceExceptionJunit4Test.java @@ -0,0 +1,48 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring; + +import org.junit.Test; +import org.junit.experimental.results.PrintableResult; +import org.quickperf.jvm.JVM; +import org.quickperf.spring.datasourceexception.SqlExceptionOfDataSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; + +public class Spring3DataSourceExceptionJunit4Test { + + @Test + public void should_fail_with_the_sql_exception_of_a_datasource_bean_refusing_the_connection() { + + assumeTrue(JVM.INSTANCE.version.isLessThanTo16()); + + // GIVEN + Class testClass = SqlExceptionOfDataSource.class; + + // WHEN + PrintableResult printableResult = PrintableResult.testResult(testClass); + + // THEN + assertThat(printableResult.failureCount()).isOne(); + + String testReport = printableResult.toString(); + // The SQLException starts a line: it is the exception of the failure, not the cause of another one. + assertThat(testReport) + .contains(System.lineSeparator() + "java.sql.SQLException: Connection refused") + .doesNotContain("UndeclaredThrowableException") + .doesNotContain("InvocationTargetException"); + + } + +} diff --git a/spring/junit4-spring3/src/test/java/org/quickperf/spring/datasourceexception/SqlExceptionOfDataSource.java b/spring/junit4-spring3/src/test/java/org/quickperf/spring/datasourceexception/SqlExceptionOfDataSource.java new file mode 100644 index 00000000..eb5f3dbe --- /dev/null +++ b/spring/junit4-spring3/src/test/java/org/quickperf/spring/datasourceexception/SqlExceptionOfDataSource.java @@ -0,0 +1,72 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.datasourceexception; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.quickperf.spring.junit4.QuickPerfSpringRunner; +import org.quickperf.spring.sql.QuickPerfProxyBeanPostProcessor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.AbstractDataSource; +import org.springframework.test.context.ContextConfiguration; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Verifies that a test getting a connection from a DataSource bean that refuses it fails + * with the SQLException thrown by the DataSource. + */ +@RunWith(QuickPerfSpringRunner.class) +@ContextConfiguration(classes = SqlExceptionOfDataSource.Config.class) +public class SqlExceptionOfDataSource { + + @Autowired + private DataSource dataSource; + + @Test + public void get_and_close_a_connection() throws SQLException { + dataSource.getConnection().close(); + } + + // Spring 3 needs a no-argument constructor to proxy this class with CGLIB. + public static class DataSourceRefusingConnections extends AbstractDataSource { + + @Override + public Connection getConnection() throws SQLException { + throw new SQLException("Connection refused"); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + throw new SQLException("Connection refused"); + } + } + + @Configuration + static class Config { + + @Bean + public DataSource dataSource() { + return new DataSourceRefusingConnections(); + } + + @Bean + public static QuickPerfProxyBeanPostProcessor dataSourceBeanPostProcessor() { + return new QuickPerfProxyBeanPostProcessor(); + } + } +} diff --git a/spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot3DataSourceExceptionJunit5Test.java b/spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot3DataSourceExceptionJunit5Test.java new file mode 100644 index 00000000..1a6f8d75 --- /dev/null +++ b/spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/SpringBoot3DataSourceExceptionJunit5Test.java @@ -0,0 +1,46 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.springboottest; + +import org.junit.jupiter.api.Test; +import org.quickperf.junit5.JUnit5Tests; +import org.quickperf.junit5.JUnit5Tests.JUnit5TestsResult; +import org.quickperf.spring.springboottest.datasourceexception.SqlExceptionOfDataSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class SpringBoot3DataSourceExceptionJunit5Test { + + @Test + void should_fail_with_the_sql_exception_of_a_datasource_bean_refusing_the_connection() { + + // GIVEN + Class testClass = SqlExceptionOfDataSource.class; + JUnit5Tests jUnit5Tests = JUnit5Tests.createInstance(testClass); + + // WHEN + JUnit5TestsResult jUnit5TestsResult = jUnit5Tests.run(); + + // THEN + assertThat(jUnit5TestsResult.getNumberOfFailures()).isOne(); + + String errorReport = jUnit5TestsResult.getErrorReport(); + // The SQLException follows "=>": it is the exception of the failure, not the cause of another one. + assertThat(errorReport) + .contains("=> java.sql.SQLException: Connection refused") + .doesNotContain("UndeclaredThrowableException") + .doesNotContain("InvocationTargetException"); + + } + +} diff --git a/spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java b/spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java new file mode 100644 index 00000000..65f94706 --- /dev/null +++ b/spring/junit5-spring-boot-3-test/src/test/java/org/quickperf/spring/springboottest/datasourceexception/SqlExceptionOfDataSource.java @@ -0,0 +1,82 @@ +/* + * 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 + * + * http://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. + * + * Copyright 2019-2022 the original author or authors. + */ +package org.quickperf.spring.springboottest.datasourceexception; + +import org.junit.jupiter.api.Test; +import org.quickperf.junit5.QuickPerfTest; +import org.quickperf.spring.springboottest.FootballApplication; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.AbstractDataSource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Verifies that a test getting a connection from a DataSource bean that refuses it fails + * with the SQLException thrown by the DataSource. + */ +@QuickPerfTest +@SpringBootTest(classes = {FootballApplication.class}) +@Import(SqlExceptionOfDataSource.Config.class) +public class SqlExceptionOfDataSource { + + @Autowired + @Qualifier("dataSourceRefusingConnections") + private DataSource dataSourceRefusingConnections; + + @Test + void get_and_close_a_connection() throws SQLException { + dataSourceRefusingConnections.getConnection().close(); + } + + static class DataSourceRefusingConnections extends AbstractDataSource { + + @Override + public Connection getConnection() throws SQLException { + throw new SQLException("Connection refused"); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + throw new SQLException("Connection refused"); + } + } + + static class Config { + + // Declaring a DataSource bean disables the one auto-configured by Spring Boot. This working + // database replaces it: JPA gets a connection from the primary DataSource at startup. + @Bean + @Primary + DataSource dataSource() { + return new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.H2) + .generateUniqueName(true) + .build(); + } + + // The DataSource of the test method, injected with @Qualifier because the other one is primary. + @Bean + DataSource dataSourceRefusingConnections() { + return new DataSourceRefusingConnections(); + } + } +} diff --git a/spring/sql-spring4/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java b/spring/sql-spring4/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java index 6b969f1e..4b325518 100644 --- a/spring/sql-spring4/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java +++ b/spring/sql-spring4/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java @@ -23,6 +23,7 @@ import javax.sql.DataSource; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -155,7 +156,13 @@ public Object invoke(final MethodInvocation invocation) throws Throwable { Method proxyMethod = ReflectionUtils.findMethod( this.datasourceProxy.getClass() ,invocation.getMethod().getName()); if (proxyMethod != null) { - return proxyMethod.invoke(this.datasourceProxy, invocation.getArguments()); + // Unwrapped so that the caller gets what the DataSource threw, such as an SQLException. + try { + return proxyMethod.invoke(this.datasourceProxy, invocation.getArguments()); + } + catch (InvocationTargetException e) { + throw e.getTargetException(); + } } return invocation.proceed(); } diff --git a/spring/sql-spring5/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java b/spring/sql-spring5/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java index d4506b2e..5703213b 100644 --- a/spring/sql-spring5/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java +++ b/spring/sql-spring5/src/main/java/org/quickperf/spring/sql/QuickPerfProxyBeanPostProcessor.java @@ -23,6 +23,7 @@ import javax.sql.DataSource; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; // Inspiration from https://blog.arnoldgalovics.com/configuring-a-datasource-proxy-in-spring-boot/ @@ -154,7 +155,13 @@ public Object invoke(final MethodInvocation invocation) throws Throwable { Method proxyMethod = ReflectionUtils.findMethod( this.datasourceProxy.getClass() ,invocation.getMethod().getName()); if (proxyMethod != null) { - return proxyMethod.invoke(this.datasourceProxy, invocation.getArguments()); + // Unwrapped so that the caller gets what the DataSource threw, such as an SQLException. + try { + return proxyMethod.invoke(this.datasourceProxy, invocation.getArguments()); + } + catch (InvocationTargetException e) { + throw e.getTargetException(); + } } return invocation.proceed(); }