@RunWith(PowerMockRunner.class)と@RunWith(SpringRunner.class)と@SpringBootTestの共存方法

## 環境
- Java 11.02
- Spring Boot 2.4.9
- powermock-module-junit4:2.0.9
- powermock-api-mockito2:2.0.7

## 概要
`@RunWith(PowerMockRunner.class)`と`@RunWith(SpringRunner.class)`を同時に使いたい。
しかし、`@RunWith`には複数クラスを指定することはできない。
そのため、以下のように`@PowerMockRunnerDelegate`を使う。


```gradle:build.gradle
dependencies {
    testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
    testImplementation 'org.powermock:powermock-api-mockito2:2.0.7'
}
```

```java
@PrepareForTest({SampleServiceImpl.class})
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@SpringBootTest
public class SampleServiceImplTest {
}
```

しかしこれでテストを実行すると以下のエラーが出る。

```
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: java.security.NoSuchAlgorithmException: class configured for TrustManagerFactory: sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory not a TrustManagerFactory
```

以下の記事では、必要なconfigurationだけ読むようにすれば解決するだろうと書いてあるが、面倒くさい。
https://stackoverflow.com/questions/50307402/exception-using-springrunner-with-powermockrunner

## 解決
以下の記事の通りに、`@PowerMockIgnore({ "javax.net.ssl.*" })`を付けたらエラーは出なくなり、テストが通るようになった。
https://www.nerd.vision/post/nosuchalgorithmexception-when-using-powermockito



```java
@PrepareForTest({ SampleServiceImpl.class })
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PowerMockIgnore({ "javax.net.ssl.*" })
@SpringBootTest
public class SampleServiceImplTest {

    @Test
    public void mainTest() throws Exception {
        SampleServiceImpl mockInstance = PowerMockito.spy(new SampleServiceImpl());
        PowerMockito.when(mockInstance, MemberMatcher.method(SampleServiceImpl.class, "private_method_name"))
                .withArguments(Mockito.anyInt()).thenReturn(0);

        mockInstance.main(Mockito.anyString());

        assertEquals(0, mockInstance.private_method_name());
    }
}
```

## まだ問題が
PowerMockitoを使ってprivateメソッドをテストする際、そのメソッド内で、Autowiredされた他のクラスのオブジェクトが呼ばれていると、そいつが`java.lang.NullPointerException`を起こす。

以下にコードの例を記載する。

### テスト対象のクラス

```java:SampleServiceImpl
@Service
public class SampleServiceImpl implementes SampleService {

    @Autowired
    SampleRepository sampleRepository;

    public void main(String message) {
        System.out.println(fetchAll());
    }

    // ここをMockにしたい
    private List<SampleModel> fetchAll() {
        // これがNullPointerExceptionを起こす。そもそもMockにしてるのに何でこいつが呼び出されるの？
        return sampleRepository.findAll();
    }
}
```

Repository

```java:SampleRepository
public interface SampleRepository extends JpaRepository<SampleModel, Long> {
}
```

Model

```java:SampleModel
@Getter
@Entity
@Table(name="sample_tables")
public class SampleModel {
    @Id
    private Long id;
    private String text;
    private Boolean status;
}
```

### テストコード

```java
@PrepareForTest({ SampleServiceImpl.class })
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PowerMockIgnore({ "javax.net.ssl.*" })
@SpringBootTest
public class SampleServiceImplTest {

  @Test
  public void mainTest() throws Exception {
        List<SampleModel> SamplesMock = SamplesMock();

        // spyにしているが、mockでも結果は同じ
        SampleServiceImpl SampleServiceImpl = PowerMockito.spy(new SampleServiceImpl());
        // 以下の行でjava.lang.NullPointerExceptionが発生する
        PowerMockito.doReturn(SamplesMock).when(SampleServiceImpl, "fetchAll");
  }
}
```

### 原因と解決方法
アノテーションが問題であった。
`@RunWith(PowerMockRunner.class)`と`@SpringBootTest`が共存しているとこの問題が起きる。
そこで、以下のように`@PowerMockIgnore`に`javax.xml.*`と`org.xml.*`を追記することで解決した。

```java
@PrepareForTest({SampleServiceImpl.class})
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PowerMockIgnore({
        "javax.net.ssl.*",
        "javax.xml.*",
        "org.xml.*"
})
@SpringBootTest
public class SampleServiceImplTest {
}
```

以下にIssueが上がっているが、Powermock 2.0.0とjdk9以上で起こるバグっぽい。。

- [Powermock 2.0.0-beta.5 problem with jdk9 modules](https://github.com/powermock/powermock/issues/864)
- [Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その１５ )( JDK を 8u202 → 11.0.2+9 に変更する )](https://ksby.hatenablog.com/entry/2019/03/16/144029?fbclid=IwAR0Ow3wvq3YS2G4YkSgY_iVHiGcLVgGp9hjs6Ppr8C6h2suM9Ozn-oroI54)