超初心者がLaravelテストでつまずいた点を書いてみた

こんにちは！
エンジニアになって2ヶ月、テストを書き出して1ヶ月半、
超初心者の私がLaravelテストを書いてつまずいた点を書いてみます。
｢こんなの知ってるよ！｣となると思いますが、
超初心者がつまずく所ってここなんだな、
という観点で見ていただけると嬉しいです。

##自己紹介
･エンジニアになって2ヶ月の超初心者(文系出身)
･前職は営業
･もちろんテスト書くのも初めて
･猫が好き

それでは、早速まとめてみようと思います！

##1.assertSee/assertDontSee


```php:AssertSee.php

$res = $this->get("url");
$res->assertStatus(200)
    ->assertDontSee('ネコちゃん');
```

仕様変更に伴い、このテストが通らなくなりました。

####状況
･変更前
　　　　if文で分岐し、条件Aに当てはまるユーザーには｢ネコちゃん｣を表示。
　　　　上記のテストでは、条件Aに当てはまらないユーザーの時、
　　　　｢ネコちゃん｣が出ないことを確認していた。

･変更後
　　　　if文ではない箇所で、｢ネコちゃん｣という文面が追加された。
　　　　そのため、どんな条件のユーザーでも｢ネコちゃん｣が出るようになった。

どうにかして、if文で出し分けている方の｢ネコちゃん｣だけが
見えていないことを確認したい。
ただ、
```assertDontSee('ネコちゃん')```にすると、通らない。

####解決策
```php:AssertSeeResolve.php
$res = $this->get("url");
$res->assertStatus("200")
    ->assertDontSee('<li>ネコちゃん</li>')
    ->assertSee('ネコちゃん');
```
if文の方の｢ネコちゃん｣は、liタグに囲まれているため、
これを含めて```assertDontSee```で調べると、うまくいきました！

####なぜ？
下記はテストを実行し、エラーが出てきた画面。
if文で出し分けているほうが、`<li>ネコちゃん</li>`です。

```
<div class="fuga">ネコちゃん</div<\n

<div class="hoge">\n
    <li>ネコちゃん</li>\n
</div>\n
```

Laravelドキュメントを見ると、```assertSee```や```assertDontSee```の定義は、
｢指定したテキストが、ページ上に存在することを宣言します。｣と書いてあります。
https://readouble.com/laravel/5.6/ja/dusk.html#assert-see

ページ上に存在する　というのが何を表しているのか分からなかったのですが、
どうやらレスポンスとして返ってきたHTMLソースを見ているようでした。
そのため、タグを含めて調べるとうまくいきました。

####ちなみに･･･
上記のような場面以外でも、
例えば想定結果が｢1｣の時、```assertSee("1")```なんかやっても
｢1｣なんかどこにでもありますよね。

そういった場合は下記のように、表示されている前後の文字列も含めて書いてあげると、
正確な```AssertSee/assertDontSee```ができます。

```php:AssertSeeResolve.php
//$hogeNumberはある処理の結果。今回は1の想定
$res = $this->get("url");
$res->assertSee("結果は".$hogeNumber."件です");
```


##2.リダイレクト後のテスト

バリデーションエラーの文言がきちんと出ているか調べるため、
リダイレクト後に```assertSee```が効いているか見ようとしました。
最初に書いたコードがこちら。

```php:RedirectTest.php
$res->assertRedirect('url')
   ->assertSee("内容を入力してください。");
```

これを実行すると、下記のようにエラーが返ってきました。

```
Failed asserting that '<!DOCTYPE html>\n
<html>\n
    <head>\n
        <meta charset="UTF-8" />\n
        <meta http-equiv="refresh" content="0;url=http://url" />\n
\n
        <title>Redirecting to http://url</title>\n
    </head>\n
    <body>\n
        Redirecting to <a href="http://url">http://url</a>.\n
    </body>\n
</html>' contains "内容を入力してください。".
```
↓かなり省略して書くと、
`Failed asserting that '(ここにレスポンス)'contains "内容を入力してください。".`
=つまり、レスポンスの中に｢内容を入力してください。｣という文字が入っていない、と言っています。

####何が返っているのか？
なぜ上記だとできないのか。
リダイレクト処理では何が返ってくるのか、が鍵になります。

リダイレクトとはつまり･･･
>HTTPヘッダにあるHTTPステータスコードにてリダイレクトの種類を伝え、Location:ヘッダで移動先を伝える

というもの。
つまり、リダイレクトして直接```assertSee```しても、
リダイレクトのレスポンス(≒移動先を伝えているところ)しか見ていない、ということになります。

####解決策

```php:RedirectTestResolve.php
$res = $this->post('url',['content' => '']); 
$res->assertRedirect('url2');
$this->get('url2')
    ->assertSee("内容を入力してください。");
```

こうすれば、リダイレクト後にgetしていることになるので、
バリデーション文言がしっかり取れます！

##3.フィールド名の変更とテストの書き方

```php:FieldName.php
$res = $this->post('url', [
            'content' => 'ニャ〜〜〜ン',
        ]);
$this->assertEquals("1", Hoge::count());
```
上記は、postした内容がきちんとDBに反映されているか見ているテストです。
ある時、フィールド名を変更したところ、上記がエラーになってしまいました。

フィールド名の変更は下記の通りです。

変更前

```
<textarea name="content">
```


変更後

```
<textarea name="hoge[content]">
```

※今回、hogeにのみ適用したいバリデーションルールがあったため、
このような実装にしました。

####解決策
```php:FieldNameResolve.php
$res = $this->post('url', [
    'hoge' => [
        'content' => 'ニャ〜〜〜ン',
    ]
]);
$this->assertEquals("1", Hoge::count());
```


この場合、`hoge[content]`と渡すようになったので、
テストでpostする際もこの形で書いてあげる必要がありました。

当たり前なのですが、急にテストでエラーが出ると最初は戸惑いました。

##4.テスト名とアノテーション

みなさん、テストのメソッド名を日本語で書けることはご存知でしょうか？
イメージはこんな感じ。

```php:JapaneseTestName.php
public function ステータステスト()
{
    $res = $this->get("url");
    $res->assertStatus(200);
}
```
ただ、このままだとテストとして認識されません。
テストとして認識してもらうには、下記の宣言が必要となります。

```php:

/**
 * @test
 */
```
つまり･･

```php:JapaneseTestName.php
/**
 * @test
 */
public function ステータステスト()
{
    $res = $this->get("url");
    $res->assertStatus(200);
}
```
こう書くとうまくいきます。

これはアノテーションと言って、
かなりざっくり言うと
>･｢注釈｣　という意味
･/** で始めて */ で終わる
･テスト実行時の振る舞いを決める

詳しくはこちらのドキュメントへ。
https://phpunit.readthedocs.io/ja/latest/annotations.html

このアノテーションを使うと、英語のテスト名の際、
Testと含まなくてもテストだと認識されます。

逆に言うと、このルールを知らずに
｢日本語名のテスト追加したら、通った！｣
｢Testを含まないメソッド名のテスト追加したら、通った！｣
と思っていたら････

<img width="500" alt="スクリーンショット 2018-10-01 21.51.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/297030/fc0f257d-80f5-6682-331f-b153562e39c6.png">

実際はテストと認識されていない(※↑テスト数が増えていない)だけだった･･！なんてことも。
~~(私は実際やらかして泣きそうになりました)~~

###ちなみに
他にもたくさんアノテーションはありますが、個人的には`@group`が便利でした。

`@group`とは、指定したテストのみを実行してくれるアノテーションで、
使う際はこのように書きます。
今回は、グループ名を`testgroup`とします。(ここはなんでもOK)

```php:GroupTest.php
/**
 * @group testgroup
 */
public function hogeTest()
{
   //処理を書く
}
```
そして、実行時は下記のように書けばOK。
`テストのファイルパス  --group グループ名`
値を入れるとこんな感じ。(あくまでイメージです。ファイルパスは環境によって異なります)
`./phpunit tests/GroupTest.php  --group testgroup`

ここだけでエラーが起こってるんだ！といった場合や、
このテストだけ検証したいんだ！という場合にとても便利でした。

##5.Factory、RefreshDatabase、IDオートインクリメント
下記はつまずいたというより、やっていく中で発見したものです。

結論を言うと、
**･RefreshDatabaseを使っても、IDのオートインクリメントはリセットされない。**
ということを発見しました。

それでは、少しずつ見ていきます。

###RefreshDatabaseとは？
各テスト(メソッド)の後にDBをリセットしてくれるトレイトです。
(詳細は下記)
https://readouble.com/laravel/5.6/ja/database-testing.html
つまり、毎回テストした後、DBをまっさらにしてくれるので
DBの影響を受けずにテストを行うことができます。

使い方は、下記を書いてあげるとOKです。

```php:HogeTest.php
use Illuminate\Foundation\Testing\RefreshDatabase;

class HogeTest extends TestCase
{
    use RefreshDatabase;

    public function testFuga()
    {
         //テストを書いていく
     }
}
```


###Factoryとは？
Factoryを用いて、テスト用の疑似データを作ることができます。
使い方は下記のとおり。

```php:FactoryTest.php
$user = factory(User::class)->create();
```
もし、カラム名を指定してデータを入れたいのであれば、

```php:FactoryTest.php
$user = factory(User::class)->create([
             'name' => 'てすとネコちゃん',
             'email' => 'test_nyan@example.com'
        ]);
```
このように指定してデータを作成することができます。

ここで指定していないカラムは、factoryを作る際にできた
`/database/factories/`以下の`○○Factory.php`ファイルの中に
初期値を指定してあげましょう。
(今回はUserFactoryになっています。)

```php:UserFactory.php
<?php

use Faker\Generator as Faker;

$factory->define(App\Daos\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'age' => '22',
        'email' => 'firstmail@example.com',
    ];
});
```
この場合、ageは先程指定していなかったので、
このファイルで指定した初期値｢22｣が適用されます。
(ちなみに`name`で使用している`$faker`は、
任意のダミーデータを作成してくれるものです。)

###オートインクリメントの関係

それでは、ざっくり説明します。

```php:AutoIncrement.php
public function hogeTest()
{
   $users = factory(User::class ,2)->create();
   var_dump($users[0]->id); //1
   var_dump($users[1]->id); //2
   var_dump($users->count()); //2
}

public function fugaTest()
{
   $users = factory(User::class ,2)->create();
   var_dump($users[0]->id); //3
   var_dump($users[1]->id); //4
   var_dump($users->count()); //2
}
```

上記を見ればわかるように、

･RefreshDatabaseのおかげでメソッドごとにDBはリセットされるため、
count()の結果は常に2になる

･ただし、DBのオートインクリメントはリセットされないため、
IDは連番になっている。

ということが分かります。

つまり、(冒頭でも述べましたが)
**･RefreshDatabaseを使っても、オートインクリメントはリセットされない。**
ということを発見しました。

｢だから？｣という感じではあるのですが、、、。
意外と周りにも知らない人がいたので書いてみました。

##最後に
かれこれ1ヶ月半テストをやってみて、
とても学ぶことが多かったです。

初心者なのでここってこういかないの！？と思うことが多々ありました。。。
少しでも私と同じような初心者の方の参考になれば幸いです！

また、ご指摘やアドバイスなどいただけるととても嬉しいです。
何卒よろしくお願い致しますm(_ _)m