CLOVERšŸ€

That was when it all began.

Spring Test恮TestExecutionListener悒試恙

恓悌ćÆ态ćŖć«ć‚’ć—ćŸćć¦ę›øć„ćŸć‚‚ć®ļ¼Ÿ

ć‚æć‚¤ćƒˆćƒ«ć©ćŠć‚Šć€Spring Test恮TestExecutionListenerćØ恄恆悂恮悒試恗恦ćæ悈恆恋ćŖćØę€ć„ć¾ć—ć¦ć€‚

Spring Test恮TestExecutionListener

TestExecutionListenerćÆSpring TestContext Framework恮äø€éƒØć§ć€ćƒ†ć‚¹ćƒˆå®Ÿč”Œę™‚ć®č¦å®šć®ć‚æć‚¤ćƒŸćƒ³ć‚°ć§å‹•ä½œć™ć‚‹ćƒŖć‚¹ćƒŠćƒ¼ć‚’čØ­å®šć™ć‚‹ć“ćØ恌
ć§ćć¾ć™ć€‚

Testing / Spring TestContext Framework / TestExecutionListener Configuration

TestExecutionListenerč‡Ŗ体ćÆć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ć™ć€‚

TestExecutionListener (Spring Framework 6.0.7 API)

TestExecutionListenerć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć—ćŸć‚Æćƒ©ć‚¹ć‚’ä½œęˆć—ć€@TestExecutionListenersć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä½æć£ć¦
ćƒ†ć‚¹ćƒˆć‚Æćƒ©ć‚¹ć«åÆ¾ć—ć¦ćƒŖć‚¹ćƒŠćƒ¼ć‚’ę§‹ęˆć—ć¾ć™ć€‚

Testing / Spring TestContext Framework / TestExecutionListener Configuration / Registering TestExecutionListener Implementations

TestExecutionListeners (Spring Framework 6.0.7 API)

TestExecutionListener恫ćÆ適ē”Ø順恌恂悊态getOrderćƒ”ć‚½ćƒƒćƒ‰ć‚’ć‚Ŗćƒ¼ćƒćƒ¼ćƒ©ć‚¤ćƒ‰ć™ć‚‹ć“ćØć§åˆ¶å¾”ć—ć¾ć™ć€‚

Testing / Spring TestContext Framework / TestExecutionListener Configuration / Ordering TestExecutionListener Implementations

順åŗć®å€¤ćŒå¤§ćć„ę–¹ćŒć‚ˆć‚Šå¾Œć§é©ē”Ø恕悌悋态ćØ恄恆恓ćØ恫ćŖć‚Šć¾ć™ć€‚

TestExecutionListenerć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć®å®Ÿč£…ćÆć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®ć‚‚ć®ćŒē”Øę„ć•ć‚Œć¦ćŠć‚Šć€ä»„äø‹ćŒé©ē”Øęøˆćæ恫ćŖć£ć¦ć„ć¾ć™ć€‚

ćƒŖć‚¹ćƒŠćƒ¼ć‚Æćƒ©ć‚¹å čŖ¬ę˜Ž order
ServletTestExecutionListener WebApplicationContextå‘ć‘ć«Servlet APIć®ćƒ¢ćƒƒć‚Æ悒꧋ꈐ恙悋 1000
DirtiesContextBeforeModesTestExecutionListener @DirtiesContextć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć®"before"ćƒ¢ćƒ¼ćƒ‰ć‚’ę‰±ć† 1500
ApplicationEventsTestExecutionListener ApplicationEventsć®ć‚µćƒćƒ¼ćƒˆć‚’ęä¾›ć™ć‚‹ 1800
DependencyInjectionTestExecutionListener ćƒ†ć‚¹ćƒˆć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć®DIć‚µćƒćƒ¼ćƒˆć‚’ęä¾›ć™ć‚‹ 2000
DirtiesContextTestExecutionListener @DirtiesContextć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć®"after"ćƒ¢ćƒ¼ćƒ‰ć‚’ę‰±ć† 3000
TransactionalTestExecutionListener @Transactionalć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä»˜äøŽć—ćŸćƒ†ć‚¹ćƒˆć‚’ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æ恕恛悋 4000
SqlScriptsTestExecutionListener @Sqlć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć§čØ­å®šć•ć‚ŒćŸSQLć‚¹ć‚ÆćƒŖćƒ—ćƒˆć‚’å®Ÿč”Œć™ć‚‹ 5000
EventPublishingTestExecutionListener ćƒ†ć‚¹ćƒˆå®Ÿč”Œć‚¤ćƒ™ćƒ³ćƒˆć‚’ćƒ†ć‚¹ćƒˆē”Ø恮ApplicationContext恫ē™ŗč”Œć™ć‚‹ 10000

Spring Testć§ęä¾›ć•ć‚Œć‚‹ę©Ÿčƒ½ć®äø­ć«ćÆć€ć“ć‚Œć‚‰ć®TestExecutionListenerć®å®Ÿč£…ć«ć‚ˆć‚Šęä¾›ć•ć‚Œć‚‹ć‚‚ć®ć‚‚ć‚ć‚‹ćŸć‚ć€ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆå†…ć«
äøŠčØ˜ć®ćƒŖć‚¹ćƒŠćƒ¼åćŒę™‚ć€…å‡ŗć¦ćć¾ć™ć€‚

TestExecutionListenerć®å®Ÿč£…ć‚’ä½œęˆć—ćŸć‚‰ć€ä»„äø‹ć®ć‚ˆć†ć«@TestExecutionListenersć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć§ęŒ‡å®šć™ć‚‹ć®ć§ć™ćŒć€‚

@SpringBootTest
@TestExecutionListeners(
        listeners = XxxTestExecutionListener.class
)
public class XxxTest {

恓悌恠ćØćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ęä¾›ć•ć‚Œć¦ć„ć‚‹ćƒŖć‚¹ćƒŠćƒ¼ćŒć™ć¹ć¦å¤–ć‚Œć¦ć—ć¾ć†ć®ć§ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®ć‚‚ć®ć‚‚å«ć‚ć¦å…ØéƒØåˆ—ęŒ™ć™ć‚‹ć‹ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®
ćƒŖć‚¹ćƒŠćƒ¼ē¾¤ćØćƒžćƒ¼ć‚ø恙悋恋悒éø恶恓ćØ恫ćŖć‚Šć¾ć™ć€‚

å¤§ęŠµć®å “åˆćÆć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®ćƒŖć‚¹ćƒŠćƒ¼ē¾¤ć‚’ćƒžćƒ¼ć‚ø恙悋恓ćØ恫ćŖ悋恮恧ćÆćØę€ć„ć¾ć™ć€‚ćć®ę–¹ę³•ć«ć¤ć„ć¦ćÆ态仄äø‹ć«ę›øć„ć¦ć‚ć‚Šć¾ć™ć€‚

Testing / Spring TestContext Framework / TestExecutionListener Configuration / Merging TestExecutionListener Implementations

仄äø‹ć®ć‚ˆć†ć«ć€MergeMode#MERGE_WITH_DEFAULTSć‚’ęŒ‡å®šć—ć¾ć™ć€‚

@SpringBootTest
@TestExecutionListeners(
    listeners = MyCustomTestExecutionListener.class,
    mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
class MyTest {
    // class body...
}

ćŖćŠć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆćÆREPLACE_DEFAULTSćŖć®ć§ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆå®Ÿč£…ćØćƒžćƒ¼ć‚øć™ć‚‹å “åˆćÆ꘎ē¤ŗēš„ć«ęŒ‡å®šć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚

ć“ć“ć¾ć§TestExecutionListenerć®å¤–č¦³ć«ć¤ć„ć¦č¦‹ć¦ćć¾ć—ćŸćŒć€ć©ć‚“ćŖć‚æć‚¤ćƒŸćƒ³ć‚°ć«å‡¦ē†ć‚’ęŒŸć‚ć‚‹ć‹ć‚’č¦‹ć¦ć„ć¾ć›ć‚“ć§ć—ćŸć­ć€‚
ć“ć‚Œć«ć¤ć„ć¦ćÆJavadoc悒čŖ­ć‚€ć—恋ćŖ恕恝恆恧恙怂

TestExecutionListener (Spring Framework 6.0.7 API)

仄äø‹ć®ćƒ”ć‚½ćƒƒćƒ‰ćŒć‚ć‚Šć¾ć™ć€‚å¼•ę•°ćÆ恄恚悌悂TestContext恧恙怂

  • beforeTestClass ā€¦ ćƒ†ć‚¹ćƒˆć‚’å®Ÿč”Œć™ć‚‹å‰ļ¼ˆćƒ†ć‚¹ćƒˆć‚Æćƒ©ć‚¹ć®ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹åŒ–ć®å‰ć€@BeforeAllćŒć‚ć‚‹å “åˆćÆćć®å‰ļ¼‰ć«å‡¦ē†ć‚’č”Œć†
  • prepareTestInstance ā€¦ ćƒ†ć‚¹ćƒˆć‚Æćƒ©ć‚¹ć®ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹åŒ–å¾Œć«å‡¦ē†ć‚’č”Œć†
  • beforeTestMethod ā€¦ ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč”Œå‰ć®ćƒ©ć‚¤ćƒ•ć‚µć‚¤ć‚Æćƒ«ć®ć‚³ćƒ¼ćƒ«ćƒćƒƒć‚Æć®å®Ÿč”Œå‰ļ¼ˆ@BeforeEachćŒć‚ć‚‹å “åˆćÆćć®å‰ļ¼‰ć«å‡¦ē†ć‚’č”Œć†
  • beforeTestExecution ā€¦ ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč”Œå‰ć«å‡¦ē†ć‚’č”Œć†
  • afterTestExecution ā€¦ ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč”Œå¾Œć«å‡¦ē†ć‚’č”Œć†
  • afterTestMethod ā€¦ ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč”Œå¾Œć®ćƒ©ć‚¤ćƒ•ć‚µć‚¤ć‚Æćƒ«ć®ć‚³ćƒ¼ćƒ«ćƒćƒƒć‚Æć®å®Ÿč”Œå¾Œļ¼ˆ@AfterEachćŒć‚ć‚‹å “åˆćÆćć®å¾Œļ¼‰ć«å‡¦ē†ć‚’č”Œć†
  • afterTestClass ā€¦ ć‚Æćƒ©ć‚¹å†…ć®ć™ć¹ć¦ć®ćƒ†ć‚¹ćƒˆć®å®Ÿč”Œå¾Œļ¼ˆ@AfterAllćŒć‚ć‚‹å “åˆćÆćć®å¾Œļ¼‰ć«å‡¦ē†ć‚’č”Œć†

ć“ć“ć¾ć§ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć‚’čŖ­ć‚“恧ćæć¾ć—ćŸćŒć€å®Ÿéš›ć«å‹•ć‹ć—ćŸę–¹ćŒę—©ć„ę°—ćŒć™ć‚‹ć®ć§č‡Ŗåˆ†ć§TestExecutionListenerć‚’ä½œć£ć¦ćæć¾ć—ć‚‡ć†ć€‚

ć¾ćšćÆ各動作ć‚æć‚¤ćƒŸćƒ³ć‚°ć‚’ē¢ŗčŖć™ć‚‹ć ć‘恮TestExecutionListenerć®å®Ÿč£…ćØć€ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć«@Transactionalć‚’ä»˜äøŽć—ć¦ćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æ
ć•ć‚Œć‚‹å‰ć«å‡¦ē†ć‚’č”Œć†TestExecutionListenerć®å®Ÿč£…ć‚’ä½œć£ć¦ćæ悋恓ćØć«ć—ć¾ć™ć€‚

ē’°å¢ƒ

ä»Šå›žć®ē’°å¢ƒćÆ态恓恔悉怂

$ java --version
openjdk 17.0.6 2023-01-17
OpenJDK Runtime Environment (build 17.0.6+10-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 17.0.6+10-Ubuntu-0ubuntu122.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.1 (2e178502fcdbffc201671fb2537d0cb4b4cc58f8)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.6, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-67-generic", arch: "amd64", family: "unix"

ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«ćÆ态MySQL悒ä½æē”Øć—ć¾ć™ć€‚

 MySQL  localhost:3306 ssl  practice  SQL > select version();
+-----------+
| version() |
+-----------+
| 8.0.32    |
+-----------+
1 row in set (0.0285 sec)

MySQLćÆ172.17.0.2ć§å‹•ä½œć—ć¦ć„ć‚‹ć‚‚ć®ćØć—ć€ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹practiceć€ć‚¢ć‚«ć‚¦ćƒ³ćƒˆćÆkazuhiraļ¼password恧ꎄē¶šć§ćć‚‹ć‚‚恮ćØ
ć—ć¾ć™ć€‚

Spring Boot惗惭ć‚ø悧ć‚Æćƒˆć‚’ä½œęˆć™ć‚‹

ć¾ćšćÆSpring Boot惗惭ć‚ø悧ć‚Æćƒˆć‚’ä½œęˆć—ć¾ć™ć€‚ä¾å­˜é–¢äæ‚恫ćÆjdbcćØmysqlć‚’åŠ ćˆć¾ć™ć€‚

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=3.0.5 \
  -d javaVersion=17 \
  -d type=maven-project \
  -d name=test-execution-listener-example \
  -d groupId=org.littlewings \
  -d artifactId=test-execution-listener-example \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.test \
  -d dependencies=jdbc,mysql \
  -d baseDir=test-execution-listener-example | tar zxvf -

ćƒ‡ć‚£ćƒ¬ć‚Æ惈ćƒŖå†…ć«ē§»å‹•ć€‚

$ cd test-execution-listener-example

Maven依存関äæ‚ćŖ恩怂

        <properties>
                <java.version>17</java.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-jdbc</artifactId>
                </dependency>

                <dependency>
                        <groupId>com.mysql</groupId>
                        <artifactId>mysql-connector-j</artifactId>
                        <scope>runtime</scope>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                </plugins>
        </build>

ē”Ÿęˆć•ć‚ŒćŸć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ćÆć€å‰Šé™¤ć—ć¦ćŠćć¾ć™ć€‚

$ rm src/main/java/org/littlewings/spring/test/TestExecutionListenerExampleApplication.java src/test/java/org/littlewings/spring/test/TestExecutionListenerExampleApplicationTests.java

mainćƒ”ć‚½ćƒƒćƒ‰ć‚’ęŒć£ćŸć‚Æćƒ©ć‚¹ćÆ再定ē¾©ć—ć¦ćŠćć¾ć—ćŸć€‚

src/main/java/org/littlewings/spring/test/App.java

package org.littlewings.spring.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }
}

ćÆć˜ć‚ć¦ć®TestExecutionListener

ęœ€åˆćÆē°”単ćŖTestExecutionListenerć®å®Ÿč£…ć‚’ä½œęˆć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

依存関äæ‚恫Spring JDBCćŒć‚ć‚‹ć¾ć¾ć ćØćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ęŽ„ē¶ščØ­å®šćŒåæ…要恫ćŖ悋恮恧态恓恓恧ćÆć„ć£ćŸć‚“å¤–ć—ć¦spring-boot-starter悒
čæ½åŠ ć—ć¦ćŠćć¾ć™ć€‚

     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-jdbc</artifactId>
       </dependency>

       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <scope>runtime</scope>
       </dependency>
       -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

TestExecutionListenerć®å®Ÿč£…ć‚’ä½œęˆć™ć‚‹ć«ćÆ态AbstractTestExecutionListenerć‚Æćƒ©ć‚¹ć‚’ē¶™ę‰æć—ć¦ä½œć‚‹ć®ćŒć‚ˆć„ćæ恟恄恧恙怂

AbstractTestExecutionListenerć‚Æćƒ©ć‚¹ćÆć€ć™ć¹ć¦ć®ćƒ”ć‚½ćƒƒćƒ‰ćŒē©ŗå®Ÿč£…ć§ć€getOrderćÆęœ€ć‚‚ä½Žć„å€¤ļ¼ˆInteger#MIN_VALUEļ¼‰ć§å®Ÿč£…ć•ć‚ŒćŸ
ć‚Æćƒ©ć‚¹ć§ć™ć€‚

AbstractTestExecutionListener (Spring Framework 6.0.7 API)

今回ćÆć™ć¹ć¦ć®ćƒ”ć‚½ćƒƒćƒ‰ć‚’å®Ÿč£…ć—ć€å‘¼ć³å‡ŗ恕悌恟惭悰å‡ŗåŠ›ć™ć‚‹TestExecutionListenerć®å®Ÿč£…ć‚’ä½œęˆć—ć¾ć—ćŸć€‚

src/test/java/org/littlewings/spring/test/listener/LoggingTestExecutionListener.java

package org.littlewings.spring.test.listener;

import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

public class LoggingTestExecutionListener extends AbstractTestExecutionListener {
    @Override
    public int getOrder() {
        return 20000;
    }

    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                "none",
                "none",
                "call, beforeTestClasses"
        );
    }

    @Override
    public void prepareTestInstance(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                testContext.getTestInstance().toString(),
                "none",
                "call, prepareTestInstance"
        );
    }

    @Override
    public void beforeTestMethod(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                testContext.getTestInstance().toString(),
                testContext.getTestMethod().getName(),
                "call, beforeTestMethod"
        );
    }

    @Override
    public void beforeTestExecution(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                testContext.getTestInstance().toString(),
                testContext.getTestMethod().getName(),
                "call, beforeTestExecution"
        );
    }

    @Override
    public void afterTestExecution(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                testContext.getTestInstance().toString(),
                testContext.getTestMethod().getName(),
                "call, afterTestExecution"
        );
    }

    @Override
    public void afterTestMethod(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                testContext.getTestInstance().toString(),
                testContext.getTestMethod().getName(),
                "call, afterTestMethod"
        );
    }

    @Override
    public void afterTestClass(TestContext testContext) throws Exception {
        log(
                testContext.getTestClass().getSimpleName(),
                "none",
                "none",
                "call, afterTestClass"
        );
    }

    void log(String testClassSimpleName, String testInstanceToString, String testMethodName, String message) {
        System.out.printf(
                "[%s/%s:%s] %s%n",
                testClassSimpleName,
                testInstanceToString,
                testMethodName,
                message
        );
    }
}

ć©ć®ćƒ”ć‚½ćƒƒćƒ‰ć®å¼•ę•°ć‚‚TestContextć§ć€ćƒ†ć‚¹ćƒˆåÆ¾č±”ć®ć‚Æćƒ©ć‚¹ć‚„ćƒ”ć‚½ćƒƒćƒ‰ć€ćƒ†ć‚¹ćƒˆē”Ø恮ApplicationContextć®å–å¾—ćŒć§ćć¾ć™ć€‚

TestContext (Spring Framework 6.0.7 API)

今回ćÆ恓恔悉悒ä½æć£ć¦ć€ćƒ†ć‚¹ćƒˆć‚Æćƒ©ć‚¹ć‚„ćƒ”ć‚½ćƒƒćƒ‰ć®ęƒ…å ±ć‚’ćƒ­ć‚°å‡ŗåŠ›ć—ć¦ć„ć¾ć™ć€‚ćŸć ć€ć‚æć‚¤ćƒŸćƒ³ć‚°ć«ć‚ˆć£ć¦ćÆå–å¾—ć§ććŖ恄悂恮悂
恂悋恮恧ļ¼ˆćŸćØ恈恰态beforeTestClass恮ć‚æć‚¤ćƒŸćƒ³ć‚°ć ćØćƒ†ć‚¹ćƒˆć‚Æćƒ©ć‚¹ć®ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ćŒćŖćć€ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć‚‚ę±ŗć¾ć‚‰ćŖ恄ļ¼‰ć€
ćć†ć„ć£ćŸć‚±ćƒ¼ć‚¹ć§ćÆå›ŗ定値none悒å‡ŗåŠ›ć™ć‚‹ć‚ˆć†ć«ć—ć¦ć„ć¾ć™ć€‚

順ē•ŖćÆć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®å®Ÿč£…ć‚ˆć‚Šć‚‚å¾Œć‚ć«ćć‚‹ć‚ˆć†ć«ć—ć¦ćŠćć¾ć—ćŸć€‚

    @Override
    public int getOrder() {
        return 20000;
    }

恓恔悉悒適ē”Øć—ćŸćƒ†ć‚¹ćƒˆć‚Æćƒ©ć‚¹ć‚’ä½œęˆć€‚

src/test/java/org/littlewings/spring/test/SampleTest.java

package org.littlewings.spring.test;

import org.junit.jupiter.api.*;
import org.littlewings.spring.test.listener.LoggingTestExecutionListener;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestExecutionListeners;

@SpringBootTest
@TestExecutionListeners(
        listeners = {LoggingTestExecutionListener.class},
        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
public class SampleTest {
    public SampleTest() {
        System.out.println("new test instance");
    }

    @BeforeAll
    static void setUpAll() {
        System.out.println("before all");
    }

    @AfterAll
    static void tearDownAll() {
        System.out.println("after all");
    }

    @BeforeEach
    void setUp() {
        System.out.println("before each");
    }

    @AfterEach
    void tearDown() {
        System.out.println("after each");
    }

    @Test
    void test1() {
        System.out.println("test1");
    }

    @Test
    void test2() {
        System.out.println("test2");
    }
}

ä½œęˆć—ćŸTestExecutionListener悒@TestExecutionListenersć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć«ęŒ‡å®šć—ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®TestExecutionListenerćƒŖć‚¹ćƒŠćƒ¼
å®Ÿč£…ćØćƒžćƒ¼ć‚øć™ć‚‹ć‚ˆć†ć«ć—ć¦ć„ć¾ć™ć€‚

@SpringBootTest
@TestExecutionListeners(
        listeners = {LoggingTestExecutionListener.class},
        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
public class SampleTest {

恂ćØćÆć€å„ēØ®ć‚³ćƒ¼ćƒ«ćƒćƒƒć‚Æćƒ”ć‚½ćƒƒćƒ‰ć‚’å®Ÿč£…ć—ć€ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć‚’2ć¤å®Ÿč£…ć—ć¦ć„ć¾ć™ć€‚

恓恮ēŠ¶ę…‹ć§ćƒ†ć‚¹ćƒˆć‚’å®Ÿč”Œć™ć‚‹ćØ态仄äø‹ć®ć‚ˆć†ćŖå‡ŗåŠ›ć«ćŖć‚Šć¾ć™ć€‚
ā€»é–¢äæ‚恮恂悋éƒØåˆ†ć®ćæć®ęŠœē²‹

ęœ€åˆć®ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč”Œć€‚

[MyTest/none:none] call, beforeTestClasses
before all
new test instance
22:45:54.406 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener -- Performing dependency injection for test class org.littlewings.spring.test.MyTest

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.5)


怜ēœē•„怜

[MyTest/org.littlewings.spring.test.MyTest@5d235104:none] call, prepareTestInstance
[MyTest/org.littlewings.spring.test.MyTest@5d235104:test1] call, beforeTestMethod
before each
[MyTest/org.littlewings.spring.test.MyTest@5d235104:test1] call, beforeTestExecution
test1
[MyTest/org.littlewings.spring.test.MyTest@5d235104:test1] call, afterTestExecution
after each
[MyTest/org.littlewings.spring.test.MyTest@5d235104:test1] call, afterTestMethod
new test instance
[MyTest/org.littlewings.spring.test.MyTest@c755b2:none] call, prepareTestInstance
[MyTest/org.littlewings.spring.test.MyTest@c755b2:test2] call, beforeTestMethod
before each
[MyTest/org.littlewings.spring.test.MyTest@c755b2:test2] call, beforeTestExecution
test2
[MyTest/org.littlewings.spring.test.MyTest@c755b2:test2] call, afterTestExecution
after each
[MyTest/org.littlewings.spring.test.MyTest@c755b2:test2] call, afterTestMethod
after all
[MyTest/none:none] call, afterTestClass

2恤ē›®ć®ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč”Œć€‚

[SampleTest/none:none] call, beforeTestClasses
before all
new test instance
[SampleTest/org.littlewings.spring.test.SampleTest@142213d5:none] call, prepareTestInstance
[SampleTest/org.littlewings.spring.test.SampleTest@142213d5:test1] call, beforeTestMethod
before each
[SampleTest/org.littlewings.spring.test.SampleTest@142213d5:test1] call, beforeTestExecution
test1
[SampleTest/org.littlewings.spring.test.SampleTest@142213d5:test1] call, afterTestExecution
after each
[SampleTest/org.littlewings.spring.test.SampleTest@142213d5:test1] call, afterTestMethod
new test instance
[SampleTest/org.littlewings.spring.test.SampleTest@934b52f:none] call, prepareTestInstance
[SampleTest/org.littlewings.spring.test.SampleTest@934b52f:test2] call, beforeTestMethod
before each
[SampleTest/org.littlewings.spring.test.SampleTest@934b52f:test2] call, beforeTestExecution
test2
[SampleTest/org.littlewings.spring.test.SampleTest@934b52f:test2] call, afterTestExecution
after each
[SampleTest/org.littlewings.spring.test.SampleTest@934b52f:test2] call, afterTestMethod
after all
[SampleTest/none:none] call, afterTestClass

ć“ć‚Œć§ć€å®Ÿč”Œé †ćŒē¢ŗčŖć§ćć¾ć—ćŸć­ć€‚

@TransactionalćŒä»˜äøŽć•ć‚Œć¦ć„悋恓ćØć‚’å‰ęć«ć—ćŸTestExecutionListenerć‚’ä½œęˆć™ć‚‹

ꬔćÆć€ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ć«@Transactionalć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ćŒä»˜äøŽć•ć‚Œć¦ć„悋恓ćØć‚’å‰ęć«ć€ćƒˆćƒ©ćƒ³ć‚¶ć‚Æć‚·ćƒ§ćƒ³ćŒćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æć•ć‚Œć‚‹å‰ć«
処ē†ć‚’č”Œć†TestExecutionListenerć®å®Ÿč£…ć‚’ä½œęˆć—ć¦ćæ悈恆ćØę€ć„ć¾ć™ć€‚

spring-boot-starter-jdbcćØmysqlć®ä¾å­˜é–¢äæ‚ć‚’å†ć³čæ½åŠ ć—ć¾ć™ć€‚

 <dependencies>
        <!--
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>
       -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

ćƒ†ćƒ¼ćƒ–ćƒ«ćÆ态schema.sqlć§ä½œęˆć™ć‚‹ć“ćØć«ć—ć¾ć™ć€‚ćŠé”ŒćÆę›øē±ć§ć€‚

src/main/resources/schema.sql

create table if not exists book(
  isbn varchar(14),
  title varchar(100),
  price int,
  primary key(isbn)
);

ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć®ęŽ„ē¶šć€SQLć®å®Ÿč”Œć€ćƒ­ć‚°ćƒ¬ćƒ™ćƒ«čØ­å®šć€‚

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
spring.datasource.username=kazuhira
spring.datasource.password=password

spring.sql.init.mode=always

logging.level.org.springframework.jdbc=DEBUG

ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ēš„ćŖć‚Æćƒ©ć‚¹ć€‚

src/main/java/org/littlewings/spring/test/Book.java

package org.littlewings.spring.test;

public class Book {
    private String isbn;
    private String title;
    private Integer price;

    public static Book create(String isbn, String title, Integer price) {
        Book book = new Book();
        book.setIsbn(isbn);
        book.setTitle(title);
        book.setPrice(price);

        return book;
    }

    // getterļ¼setterćÆēœē•„
}

Serviceć‚Æćƒ©ć‚¹ć‚‚ä½œęˆć€‚

src/main/java/org/littlewings/spring/test/BookService.java

package org.littlewings.spring.test;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;

@Service
@Transactional
public class BookService {
    NamedParameterJdbcTemplate jdbcTemplate;

    public BookService(NamedParameterJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void add(Book book) {
        jdbcTemplate.update(
                """
                        insert into book(isbn, title, price)
                        values(:isbn, :title, :price)""",
                new BeanPropertySqlParameterSource(book)
        );
    }

    public Book findByIsbn(String isbn) {
        return jdbcTemplate.queryForObject(
                """
                        select isbn, title, price
                        from book
                        where isbn = :isbn""",
                Map.of("isbn", isbn),
                new BeanPropertyRowMapper<>(Book.class)
        );
    }
}

1件ē™»éŒ²ć€1ä»¶å–å¾—ć€‚ä»Šå›žęœ€ä½Žé™ä½æ恆悂恮恠恑恧恙怂

TestExecutionListenerć‚’ä½œęˆć—ć¾ć™ć€‚

src/test/java/org/littlewings/spring/test/listener/WithTestTransactionExecutionListener.java

package org.littlewings.spring.test.listener;

import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

public class WithTestTransactionExecutionListener extends AbstractTestExecutionListener {
    @Override
    public int getOrder() {
        return 30000;
    }

    @Override
    public void afterTestMethod(TestContext testContext) throws Exception {
        System.out.println("WithTestTransactionExecutionListener:afterTestMethod");

        NamedParameterJdbcTemplate jdbcTemplate =
                testContext.getApplicationContext().getBean(NamedParameterJdbcTemplate.class);

        assertThat(jdbcTemplate.queryForObject(
                """
                        select title
                        from book
                        where isbn = :isbn""",
                Map.of("isbn", "978-4798142470"),
                String.class))
                .isEqualTo("Springå¾¹åŗ•å…„門 Spring Frameworkć«ć‚ˆć‚‹Javać‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³é–‹ē™ŗ");
        assertThat(jdbcTemplate.queryForObject(
                """
                        select title
                        from book
                        where isbn = :isbn""",
                Map.of("isbn", "978-4774182179"),
                String.class))
                .isEqualTo("[ę”¹čØ‚ę–°ē‰ˆ]Springå…„é–€ ā€•ā€•Javaćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æćƒ»ć‚ˆć‚Šč‰Æ恄čØ­č؈ćØć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£");
    }
}

順ē•ŖćÆć€å°‘ćŖ恏ćØ悂TransactionalTestExecutionListener恮4000ć‚ˆć‚Šå¤§ćć„åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ć ć„ć¶å¤§ććć—ć¾ć—ćŸćŒć€‚

    @Override
    public int getOrder() {
        return 30000;
    }

ć‚Ŗćƒ¼ćƒćƒ¼ćƒ©ć‚¤ćƒ‰ć™ć‚‹ćƒ”ć‚½ćƒƒćƒ‰ćÆ态afterTestMethodć«ć—ć¾ć—ćŸć€‚ć“ć‚ŒćÆ态TransactionalTestExecutionListenerćŒćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æć‚’č”Œć†ć®ćŒ
afterTestMethodćŖć®ć§ć“ć‚Œć«åˆć‚ć›ć¦ć„ć¾ć™ć€‚

    @Override
    public void afterTestMethod(TestContext testContext) throws Exception {

å®Ÿč£…ćØ恗恦ćÆć€å‘¼ć³å‡ŗć•ć‚Œć¦ć„ć‚‹ć“ćØ恮ē¢ŗčŖćØć€ć‚¢ć‚µćƒ¼ć‚·ćƒ§ćƒ³ć€‚

    @Override
    public void afterTestMethod(TestContext testContext) throws Exception {
        System.out.println("WithTestTransactionExecutionListener:afterTestMethod");

        NamedParameterJdbcTemplate jdbcTemplate =
                testContext.getApplicationContext().getBean(NamedParameterJdbcTemplate.class);

        assertThat(jdbcTemplate.queryForObject(
                """
                        select title
                        from book
                        where isbn = :isbn""",
                Map.of("isbn", "978-4798142470"),
                String.class))
                .isEqualTo("Springå¾¹åŗ•å…„門 Spring Frameworkć«ć‚ˆć‚‹Javać‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³é–‹ē™ŗ");
        assertThat(jdbcTemplate.queryForObject(
                """
                        select title
                        from book
                        where isbn = :isbn""",
                Map.of("isbn", "978-4774182179"),
                String.class))
                .isEqualTo("[ę”¹čØ‚ę–°ē‰ˆ]Springå…„é–€ ā€•ā€•Javaćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æćƒ»ć‚ˆć‚Šč‰Æ恄čØ­č؈ćØć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£");
    }

ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ēµ‚äŗ†ę™‚ć«ćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æć•ć‚Œć‚‹å‰ć§ć‚ć‚Œć°ć€ć‚³ćƒŸćƒƒćƒˆå‰ć®ćƒ‡ćƒ¼ć‚æćŒč¦‹ćˆć‚‹ćÆ恚恧恙怂

ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć€‚

src/test/java/org/littlewings/spring/test/TransactionalListenerTest.java

package org.littlewings.spring.test;

import org.junit.jupiter.api.Test;
import org.littlewings.spring.test.listener.WithTestTransactionExecutionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.transaction.annotation.Transactional;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@TestExecutionListeners(
        listeners = {WithTestTransactionExecutionListener.class},
        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
public class TransactionalListenerTest {
    @Autowired
    BookService bookService;

    @Test
    @Transactional
    void withTransaction() {
        bookService.add(Book.create("978-4798142470", "Springå¾¹åŗ•å…„門 Spring Frameworkć«ć‚ˆć‚‹Javać‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³é–‹ē™ŗ", 4400));
        bookService.add(Book.create("978-4774182179", "[ę”¹čØ‚ę–°ē‰ˆ]Springå…„é–€ ā€•ā€•Javaćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æćƒ»ć‚ˆć‚Šč‰Æ恄čØ­č؈ćØć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£", 4180));

        assertThat(bookService.findByIsbn("978-4798142470").getTitle())
                .isEqualTo("Springå¾¹åŗ•å…„門 Spring Frameworkć«ć‚ˆć‚‹Javać‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³é–‹ē™ŗ");
        assertThat(bookService.findByIsbn("978-4774182179").getTitle())
                .isEqualTo("[ę”¹čØ‚ę–°ē‰ˆ]Springå…„é–€ ā€•ā€•Javaćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æćƒ»ć‚ˆć‚Šč‰Æ恄čØ­č؈ćØć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£");
    }
}

@Transactionalć‚¢ćƒŽćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä»˜äøŽć—ć¦ć„ć‚‹ć®ć§ć€ćƒ†ć‚¹ćƒˆćƒ”ć‚½ćƒƒćƒ‰ēµ‚äŗ†ę™‚ć«ćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æć—ć¾ć™ć€‚

    @Test
    @Transactional
    void withTransaction() {

恓恮ēŠ¶ę…‹ć§ć€ćƒ†ć‚¹ćƒˆć‚’å®Ÿč”Œć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

ä½œęˆć—ćŸTestExecutionListenerćŒé©ē”Øć•ć‚Œć¦ć„ć‚‹ćƒ­ć‚°ćŒå‡ŗåŠ›ć•ć‚Œć¾ć™ć€‚

00:49:04.546 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener -- Before test class: class [TransactionalListenerTest], class annotated with @DirtiesContext [false] with mode [null]

Spring JDBCć®ćƒ­ć‚°ćƒ¬ćƒ™ćƒ«ć‚’DEBUGć«ć—ć¦ć„ć‚‹ć®ć§ć€ä»Šå›žä½œęˆć—ćŸTestExecutionListenerć®å¾Œć«ćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚Æć•ć‚Œć¦ć„ć‚‹ć“ćØ恌
ē¢ŗčŖć§ćć¾ć™ć€‚

WithTestTransactionExecutionListener:afterTestMethod
2023-03-25T00:49:07.497+09:00 DEBUG 95328 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-03-25T00:49:07.497+09:00 DEBUG 95328 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [select title
from book
where isbn = ?]
2023-03-25T00:49:07.499+09:00 DEBUG 95328 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-03-25T00:49:07.499+09:00 DEBUG 95328 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [select title
from book
where isbn = ?]
2023-03-25T00:49:07.501+09:00 DEBUG 95328 --- [           main] o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction rollback
2023-03-25T00:49:07.501+09:00 DEBUG 95328 --- [           main] o.s.jdbc.support.JdbcTransactionManager  : Rolling back JDBC transaction on Connection [HikariProxyConnection@773301025 wrapping com.mysql.cj.jdbc.ConnectionImpl@66b59b7d]
2023-03-25T00:49:07.521+09:00 DEBUG 95328 --- [           main] o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@773301025 wrapping com.mysql.cj.jdbc.ConnectionImpl@66b59b7d] after transaction

ćƒ‡ćƒ¼ć‚æć‚‚ę®‹ć£ć¦ć„ć¾ć›ć‚“ć­ć€‚

 MySQL  localhost:3306 ssl  practice  SQL > select * from book;
Empty set (0.0760 sec)

恓恓恧試恗恫态TransactionalTestExecutionListenerć‚ˆć‚Šć‚‚å…ˆć«é©ē”Øć•ć‚Œć‚‹ć‚ˆć†ć«ć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

public class WithTestTransactionExecutionListener extends AbstractTestExecutionListener {
    @Override
    public int getOrder() {
        //return 30000;
        return 300;
    }

恓恮ēŠ¶ę…‹ć§ćƒ†ć‚¹ćƒˆć‚’å®Ÿč”Œć™ć‚‹ćØć€ćƒ­ćƒ¼ćƒ«ćƒćƒƒć‚ÆćŒå…ˆć«č”Œć‚ć‚Œć‚‹ćŸć‚TestExecutionListenerå†…ć§ć®ć‚¢ć‚µćƒ¼ć‚·ćƒ§ćƒ³ć«å¤±ę•—ć—ć¾ć™ć€‚

2023-03-25T00:52:42.730+09:00 DEBUG 95575 --- [           main] o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction rollback
2023-03-25T00:52:42.730+09:00 DEBUG 95575 --- [           main] o.s.jdbc.support.JdbcTransactionManager  : Rolling back JDBC transaction on Connection [HikariProxyConnection@1972628089 wrapping com.mysql.cj.jdbc.ConnectionImpl@31c628e7]
2023-03-25T00:52:42.743+09:00 DEBUG 95575 --- [           main] o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@1972628089 wrapping com.mysql.cj.jdbc.ConnectionImpl@31c628e7] after transaction
WithTestTransactionExecutionListener:afterTestMethod
2023-03-25T00:52:42.745+09:00 DEBUG 95575 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-03-25T00:52:42.745+09:00 DEBUG 95575 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [select title
from book
where isbn = ?]
2023-03-25T00:52:42.745+09:00 DEBUG 95575 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
2023-03-25T00:52:42.747+09:00  WARN 95575 --- [           main] o.s.test.context.TestContextManager      : Caught exception while invoking 'afterTestMethod' callback on TestExecutionListener [org.littlewings.spring.test.listener.WithTestTransactionExecutionListener] for test method [void org.littlewings.spring.test.TransactionalListenerTest.withTransaction()] and test instance [org.littlewings.spring.test.TransactionalListenerTest@58687fb7]

org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
        at org.springframework.dao.support.DataAccessUtils.nullableSingleResult(DataAccessUtils.java:97) ~[spring-tx-6.0.7.jar:6.0.7]
        at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:244) ~[spring-jdbc-6.0.7.jar:6.0.7]
        at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:252) ~[spring-jdbc-6.0.7.jar:6.0.7]
        at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:268) ~[spring-jdbc-6.0.7.jar:6.0.7]
        at org.littlewings.spring.test.listener.WithTestTransactionExecutionListener.afterTestMethod(WithTestTransactionExecutionListener.java:25) ~[test-classes/:na]
        at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:440) ~[spring-test-6.0.7.jar:6.0.7]
        at org.springframework.test.context.junit.jupiter.SpringExtension.afterEach(SpringExtension.java:206) ~[spring-test-6.0.7.jar:6.0.7]

怜ēœē•„怜

恓悌恧态ē¢ŗčŖć—ćŸć„ć“ćØćÆ恧恍恟恋ćŖćØę€ć„ć¾ć™ć€‚

恔ćŖćæć«ć€å®ŸćÆä»Šå›žć®ä¾‹ć ćØć‚Ŗćƒ¼ćƒćƒ¼ćƒ©ć‚¤ćƒ‰ć™ć‚‹ćƒ”ć‚½ćƒƒćƒ‰ć‚’afterTestExecution恫恙悋ćØ态TransactionalTestExecutionListenerć‚ˆć‚Šå‰ć«
適ē”Øć•ć‚Œć‚‹ć‚ˆć†ć«ć—ć¦ć‚‚ćƒ†ć‚¹ćƒˆć«å¤±ę•—ć—ćŖ恏ćŖć£ćŸć‚Šć—ć¾ć™ć€‚
afterTestExecutionćÆ态afterTestMethodć‚ˆć‚Šć‚‚å‰ć«å‹•ä½œć™ć‚‹ć‹ć‚‰ć§ć™ć­ć€‚

今回ćÆ态適ē”Ø順恮ē¢ŗčŖćØ恄恆恓ćØ恧怂

ć¾ćØ悁

Spring Test恮TestExecutionListener悒試恗恦ćæć¾ć—ćŸć€‚

存åœØ悒ēŸ„悉ćŖć‹ć£ćŸć®ć§ć€Spring Frameworkć§ć®ćƒ†ć‚¹ćƒˆć®ę‹”å¼µę–¹ę³•ć®ć²ćØ恤悒ēŸ„悋č‰Æć„ę©Ÿä¼šć«ćŖć‚Šć¾ć—ćŸć€‚Spring Frameworkē”Ø恫ćÆ
ćŖć‚Šć¾ć™ćŒć€JUnit 5悈悊悂悈悊ē“°ć‹ććƒ©ć‚¤ćƒ•ć‚µć‚¤ć‚Æćƒ«ć«å‡¦ē†ć‚’ęŒŸćæč¾¼ć‚ć‚‹ć®ć§ć€ć‚„ć‚ŠćŸć„ć“ćØ恫åæœć˜ć¦ä½æć„åˆ†ć‘ć‚‹ę„Ÿć˜ć§ć™ć­ć€‚