Googleが提唱するTestSizeとJava,MavenによるTestSizeの実現方法について

# TestSizeとは

[テストから見えてくる グーグルのソフトウェア開発](https://www.amazon.co.jp/dp/B00IE3B522/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)や[Google Testing Blog](https://testing.googleblog.com/2010/12/test-sizes.html)によると、Googleは開発者のテスト区分を統一する（それまではシステムテスト、EtoEテスト、UIテスト、Seleniumテストなどいろいろな表現があり、そのどれもがどのようなテストか明確に定義されない）ために、TestSizeという概念と導入し、それぞれ以下のように定義している。
 
 
|      |S(Small)テスト|M(Medium)テスト|L(Large)テスト|E(Enormous)テスト|
|------|------|------|------|------|
|時間的目標（メソッドごと） |100ms未満で実行|1s 未満で実行|できる限り高速に実行|できる限り高速に実行|
|実行時間の上限|起動後１分経ったらSテストターゲットを強制終了|起動後５分経ったらMテストターゲットを強制終了|起動後１５分経ったらLテストターゲットを強制終了|起動後１時間経ったらEテストターゲットを強制終了|
 
|     |Sテスト|Mテスト|Lテスト|Eテスト|
|------|------|------|------|------|
|ネットワークサービス（ソケットオープン） |モック|localhostのみ|○|○|
|データベース|モック|○|○|○|
|ファイルシステムアクセス|モック|○|○|○|
|ユーザと向かい合うシステムへのアクセス|モック|推奨されない|○|○|
|システムコール実行|×|推奨されない|○|○|
|マルチスレッド|推奨されない|○|○|○|
|sleep 文|×|○|○|○|
|システムプロパティ|×|○|○|○|

# TestSizeによる実行タイミングの分割 

このTestSizeごとにテストの実行タイミングを定義することで、必要なフェーズで必要な分のテストが実行でき、素早くユーザへフィードバックすることができるようになる。 

例えば、[クックパッドの記事](http://techlife.cookpad.com/entry/2016/08/13/test-size-for-mobile)にならうと、以下のように実行タイミングをわけることができる。
このようにすることで、クックパッドではプルリクエストでテスト実行を５分以内に行い、早期にユーザへフィードバックすることを実現している。

|     |Sテスト|Mテスト|Lテスト|Eテスト|
|------|------|------|------|------|
|実行タイミング|プルリクエストごとに|プルリクエストごと、もしくはMasterにマージされるごとに|任意、もしくはリリースごとに|任意、もしくはリリースごとに|


# Java,MavenによるTestSizeの実現

では、TestSizeとその実行タイミングの分割をどのように実現するか。
ここではJava,Mavenを使用した場合を解説する。

## 環境
* Java:1.8.0_102
* Apache Maven:3.3.9
* JUnit:4.12
* maven-surefire-plugin:2.19.1

## Categoryクラスとアノテーションの付与
Javaの代表的なテストフレームワークであるJUnitには、JUnit4.8からCategoryというアノテーションが追加された。
テスト対象のメソッドやクラスに`@Category`をマークすることで、テストスイートから実行する対象を細かく制御できるようになる。

詳しくはこの[記事](http://etc9.hatenablog.com/entry/20100104/1262630003?amp=1)を参照してほしい。

TestSizeごとのインターフェースを定義して、
定義したインターフェースを`@Category`の引数に渡すことでテスト実行対象を制御することができる。

```java
package hogehoge;

public interface Small {}
```

```java
package hogehoge;

public interface Medium {}
```

```java
public class hoge {
    @Category(Small.class)
    @Test
    public void a() {
        fail();
    }

    @Category(Medium.class)
    @Test
    public void b() {
        fail();
    }
}
```


## MavenによるTestSizeの指定方法

Mavenの実行時にTestSizeをパラメータとして渡せれば、TestSizeによる実行タイミングの分割は可能である。

できればこのようにできるとよい。
TestSizeの指定がなければ、全てのテストを実行し、TestSizeを一つもしくは複数指定でき、その場合は指定したTestSizeのテストが実行される。

```
mvn test // All tests run.
mvn test -P Small // Small tests run.
mvn test -P Small,Medium // Small and medium tests run.
```

上記を実現するMavenの設定は以下のようになる。
`maven-surefire-plugin`の`groups`にTestSizeを指定する。
デフォルトでは空を指定することで、引数を指定しない場合にすべてのテストが実行されるようにしている。

```xml
<project ...>
    <properties>
        <testcase.groups></testcase.groups>
    </properties>

    <profiles>
        <profile>
            <id>Small</id>
            <properties>
                <testcase.groups>hogehoge.Small</testcase.groups>
            </properties>
        </profile>
        <profile>
            <id>Medium</id>
            <properties>
                <testcase.groups>hogehoge.Medium</testcase.groups>
            </properties>
        </profile>
    </profiles>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                    <groups>${testcase.groups}</groups>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
```

# まとめ

TestSizeの話はGoogle,クックパッドさんがよく記事にしてくれていますが、その実現方法について記載した記事がなかったので、記載しました。
何かの役にたてば光栄です。