composer testでQAツールの実行

# 概要

`phpunit`, `phpcs`, `phpmd`を`composer test`コマンドで簡単に実行できるようにします。

# 準備

※ 現在のBEAR.Sundayのアプリケーションスケルトンはこの準備は完了していています。

`composer.json`に以下のエントリーを記述します。

```json
    "require-dev": {
        "phpunit/phpunit": "~4.8",
        "squizlabs/php_codesniffer": "~2.3",
        "phpmd/phpmd": "~2.3"
    },
    "scripts" :{
        "test": [
            "php vendor/phpmd/phpmd/src/bin/phpmd src text ./phpmd.xml",
            "php vendor/squizlabs/php_codesniffer/scripts/phpcs",
            "php vendor/phpunit/phpunit/phpunit"
        ]
    }
```

`phpunit.xml.dist`, `phpcs.xml`, `phpmd.xml`ファイルを用意します。

```phpunit.xml.dist
<phpunit bootstrap="vendor/autoload.php">
    <testsuites>
        <testsuite>
            <directory suffix="Test.php">tests</directory>
        </testsuite>
    </testsuites>

    <logging>
        <log type="coverage-html" target="build/coverage"/>
        <log type="coverage-clover" target="build/logs/clover.xml"/>
        <log type="junit" target="build/logs/junit.xml" logIncompleteSkipped="false"/>
    </logging>

    <filter>
        <whitelist>
            <directory suffix=".php">src</directory>
        </whitelist>
    </filter>
</phpunit>
```

```phpcs.xml
<?xml version="1.0"?>
<ruleset>
    <description>The coding standard used for applications using BEAR.Sunday Framework.</description>
    <rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
    <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
    <rule ref="Generic.Strings.UnnecessaryStringConcat"/>
    <rule ref="PSR2">
        <exclude name="Generic.Files.LineLength"/>
        <exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
        <exclude name="PSR2.Methods.MethodDeclaration.Underscore"/>
    </rule>
    <rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
    <rule ref="PEAR.Commenting.FunctionComment">
        <exclude name="PEAR.Commenting.FunctionComment.MissingReturn"/>
        <exclude name="PEAR.Commenting.FunctionComment.MissingParamComment"/>
        <exclude name="PEAR.Commenting.FunctionComment.SpacingBeforeTags"/>
        <exclude name="PEAR.Commenting.FunctionComment.MissingParamTag"/>
        <exclude name="PEAR.Commenting.FunctionComment.Missing"/>
        <exclude name="PEAR.Commenting.FunctionComment.MissingReturn"/>
        <exclude name="PEAR.Commenting.FunctionComment.ParameterCommentsNotAligned"/>
        <exclude name="PEAR.Commenting.FileComment.Missing"/>
    </rule>
    <file>src</file>
    <file>tests</file>
</ruleset>
```

```phpmd.xml
<ruleset
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://pmd.sf.net/ruleset/1.0.0"
        xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
        xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
    <!--codesize-->
    <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
        <properties>
            <property name="reportLevel" value="20"/>
        </properties>
    </rule>
    <rule ref="rulesets/codesize.xml/NPathComplexity">
        <properties>
            <property name="minimum" value="200"/>
        </properties>
    </rule>
    <rule ref="rulesets/codesize.xml/ExcessiveClassComplexity">
        <properties>
            <property name="maximum" value="100"/>
        </properties>
    </rule>
    <rule ref="rulesets/codesize.xml/ExcessiveClassLength"/>
    <rule ref="rulesets/codesize.xml/ExcessiveMethodLength"/>
    <rule ref="rulesets/codesize.xml/ExcessiveParameterList"/>
    <rule ref="rulesets/codesize.xml/ExcessivePublicCount"/>
    <rule ref="rulesets/codesize.xml/TooManyFields"/>
    <rule ref="rulesets/codesize.xml/TooManyMethods"/>
    <!--design-->
    <rule ref="rulesets/design.xml/EvalExpression"/>
    <rule ref="rulesets/design.xml/ExitExpression"/>
     <rule ref="rulesets/design.xml/GotoStatement" />
    <rule ref="rulesets/design.xml/NumberOfChildren"/>
    <rule ref="rulesets/design.xml/DepthOfInheritance"/>
    <!-- <rule ref="rulesets/design.xml/CouplingBetweenObjects" /> -->
    <!--naming-->
    <rule ref="rulesets/naming.xml/ConstantNamingConventions"/>
    <rule ref="rulesets/naming.xml/BooleanGetMethodName"/>
    <!--unusedcode-->
    <rule ref="rulesets/unusedcode.xml/UnusedFormalParameter"/>
    <rule ref="rulesets/unusedcode.xml/UnusedLocalVariable"/>
    <rule ref="rulesets/unusedcode.xml/UnusedPrivateField"/>
    <rule ref="rulesets/unusedcode.xml/UnusedPrivateMethod"/>
    <!--controversial-->
    <rule ref="rulesets/controversial.xml/CamelCaseClassName"/>
    <rule ref="rulesets/controversial.xml/CamelCasePropertyName"/>
    <rule ref="rulesets/controversial.xml/CamelCaseMethodName"/>
    <!--cleancode-->
    <rule ref="rulesets/cleancode.xml/BooleanArgumentFlag"/>
    <!-- <rule ref="rulesets/cleancode.xml/ElseExpression" /> -->
</ruleset>
```

## phpunitの実行

`vendor/bin/phpunit`で実行したユニットテストは`build/coverage/index.html`でテストカバレッジが確認できます。

```
$ vendor/bin/phpunit 
....

Time: 2.76 seconds, Memory: 14.50Mb

OK (4 tests, 5 assertions)

Generating code coverage report in Clover XML format ... done

Generating code coverage report in HTML format ... done
```

<img width="1175" alt="スクリーンショット 2015-12-01 21.59.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/3940/5453e439-ec0d-37ef-ae95-8d6d387d34a1.png">

## composer testの実行

`composer test`コマンドで`phpunit`だけでなく、`phpcs`や`phpmd`を合わせて実行することができます。

```
composer test
> php vendor/phpmd/phpmd/src/bin/phpmd src text ./phpmd.xml
> php vendor/squizlabs/php_codesniffer/scripts/phpcs
> php vendor/phpunit/phpunit/phpunit
```

## 設定値を調整する

`phpmd`や`phpcs`の設定ファイルを自分のプロジェクトの方針に合わせて調整します。

### phpmd

`CyclomaticComplexity`, `NPathComplexity`, `ExcessiveClassComplexity`はプログラムの複雑さ(<!--codesize-->)についての値です。`<properties>`タグの値を半分にするとデフォルトの厳しい値になります。

「`else`句は使わない」というルールを採用する場合は`<rule ref="rulesets/cleancode.xml/ElseExpression" />`のコメントを外します。
(BEAR.Sundayは全パッケージで採用しています。なぜ`else`を問題にするかは http://tkramar.blogspot.jp/2008/01/else-is-evil.html を参照してください。）

ルールの詳細はhttp://phpmd.org/rules/index.html をご覧ください。

## phpcs

phpcsは`src/`と`tests/`フォルダを対象にしています。
ルールの詳細はhttps://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml をご覧ください。

## 活用ヒント

### git hook

commit時にテストが実行されます。

```lang:.git/hooks/pre-commit
#!/bin/sh

composer test
```

### phpStormのExternal Toolsで

<img width="1287" alt="スクリーンショット 2015-12-02 19.09.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/3940/5f0d2a7b-687e-163f-b964-349a4b913ed1.png">

Tools > External Tools > QA Toolsで実行できます。