【JUnit5】テストメソッドが終了すると呼び出したスレッドも強制終了される

JUnit5のテストメソッドから別スレッドを起動した場合の挙動についてです。

通常は呼び出し元のメソッドが終わっても、呼び出し先のスレッド処理は（そのスレッド自身の処理が終わらない限り）続行されます。

しかしJUnit5のテストメソッドからスレッド起動させた場合は、テストメソッドが生きている間しか動かない挙動になります。（JUnitのドキュメントはちゃんと確認していませんが、さんざん試した結果）

以下コード（※Spring Bootなどのフレームワークは用いないピュアなJavaプロジェクトにて実行）。
コンソール出力と合わせてディレクトリ作成処理を入れて二重にアウトプット確認します。
（ループ2周目でFiles.createDirectoryがエラーになるの見え見えですが本題とは関係ないので気にしない）

### Threadクラスの例

```java:src/main/java配下
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;

public class SampleThread extends Thread{
    
    @Override
    public void run() {
        System.out.println("スレッドが起動しました。" + LocalDateTime.now());
        try{
            while (true) {
                Thread.sleep(1000);
                System.out.println("スレッド実行中。"  + LocalDateTime.now());
                Path path1 = Paths.get("src/test/resources/tekito");
                Files.createDirectory(path1);
            }
        }catch(Exception e){
            System.out.println("スレッドが終了しました。" + LocalDateTime.now());
        }
    }
}
```

```java:src/test/java配下
import java.time.LocalDateTime;
import org.junit.jupiter.api.Test;
import com.example.SampleThread;

public class SampleThreadTest {
    
    @Test
    public void threadTest(){
        System.out.println("テストメソッド開始；" + LocalDateTime.now());
        SampleThread th = new SampleThread();
        th.start();
        System.out.println("テストメソッド終了：" + LocalDateTime.now());
    }
}
```
```:実行結果
テストメソッド開始；2024-02-21T21:35:19.473247100
テストメソッド終了：2024-02-21T21:35:19.476725900
スレッドが起動しました。2024-02-21T21:35:19.476725900
```
スレッド起動から1秒後に「スレッド実行中。」と出力されませんでした。
またsrc/test/resources/tekitoディレクトリも作成されませんでした。

### Runnableインタフェースの例

```java:src/main/java配下
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;

public class SampleRunable implements Runnable{

    @Override
    public void run() {
        System.out.println("スレッドが起動しました。：" + LocalDateTime.now());
        try{
            while (true) {
                Thread.sleep(1000);
                System.out.println("スレッド実行中。" + LocalDateTime.now());
                Path path1 = Paths.get("src/test/resources/tekito");
                Files.createDirectory(path1);
            }
        }catch(Exception e){
            System.out.println("スレッドが終了しました。：" + LocalDateTime.now());
        }
    }
}
```

```java:src/test/java配下
import java.time.LocalDateTime;
import org.junit.jupiter.api.Test;
import com.example.SampleRunable;

public class SampleRunableTest {
    
    @Test
    public void runnableTest(){
        System.out.println("テストメソッド開始：" + LocalDateTime.now());
        Thread th = new Thread(new SampleRunable());
        th.start();
        System.out.println("テストメソッド終了：" + LocalDateTime.now());
    }
}
```

```:実行結果
テストメソッド開始：2024-02-21T21:36:01.670586800
テストメソッド終了：2024-02-21T21:36:01.671598800
スレッドが起動しました。：2024-02-21T21:36:01.672593100
```
スレッド起動から1秒後に「スレッド実行中。」と出力されませんでした。
またsrc/test/resources/tekitoディレクトリも作成されませんでした。

### ScheduledExecutorServiceインタフェースの例

```java:src/main/java配下
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceSample {

    public void sample() throws Exception {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

        service.schedule(() -> {
            System.out.println("end!!" + LocalDateTime.now());
            service.shutdown();
            System.exit(0);
        }, 3, TimeUnit.SECONDS);
    }
}
```


```java:src/test/java配下
import java.time.LocalDateTime;
import org.junit.jupiter.api.Test;
import com.example.ScheduledExecutorServiceSample;

public class ScheduledExecutorServiceTest {
    
    @Test
    public void main() throws Exception {
        System.out.println("テストメソッド開始：" + LocalDateTime.now());
        ScheduledExecutorServiceSample sess = new ScheduledExecutorServiceSample();
        sess.sample();
        System.out.println("テストメソッド終了：" + LocalDateTime.now());
    }
}
```
```:実行結果
テストメソッド開始：2024-02-21T21:36:41.433970700
テストメソッド終了：2024-02-21T21:36:41.436966300
```
3秒後に「end!!」と出力されませんでした。
またsrc/test/resources/tekitoディレクトリも作成されませんでした。

## まとめ
なので、呼び出し先のスレッドを生かしておきたい場合は、テストメソッドのスレッドもその分延命する必要があるということになります。

```java
import java.time.LocalDateTime;
import org.junit.jupiter.api.Test;
import com.example.SampleThread;

public class SampleThreadTest {
    
    @Test
    public void threadTest() throws InterruptedException{
        System.out.println("テストメソッド開始；" + LocalDateTime.now());
        SampleThread th = new SampleThread();
        th.start();
        Thread.sleep(3000);
        System.out.println("テストメソッド終了：" + LocalDateTime.now());
    }
}
```

```:出力結果
テストメソッド開始；2024-02-21T21:57:25.270851200
スレッドが起動しました。2024-02-21T21:57:25.272786200
スレッド実行中。2024-02-21T21:57:26.277437200
スレッド実行中。2024-02-21T21:57:27.284217200
スレッドが終了しました。2024-02-21T21:57:27.285999
テストメソッド終了：2024-02-21T21:57:28.280090700
```