あじちゃんの備忘録。

〜ここはメモ帳です

日付フォーマット

date()とstoredtime()を組み合わせる

  • ただ出力する場合

🙂💬 strtotime()でUnixタイムスタンプを取得し、date()で日付のフォーマットを変更しています。

<?php
// === hoge.php === //

$date = '2018-05-28 00:00:00';
echo date('Y/m/d', strtotime($date));
  • bladeから出力する場合
    (やってることもみためも一緒)
<?php
// === hoge.Controller.php === //

public function index()
{
    //modelからデータを取得
    $data = hogeModel::selectAll();
    
    return view('hoge.index', ['data' => $data]);
}
<!-- === hoge.blade.php === -->

<p>{{ date('Y-m-d', strtotime($data['date'])) }}</p>

🗒 フォーマットは公式を参照するのが一番良い: php公式-date

PHPUnitでテストを行う

Testメソッド作成

  • makeでTestUnitを作成
$ php artisan make:test [TestUnitName]

🙂💬 作成したTestUnitは[tests/Feature/]配下に配置されるので、機能が分かれている時は[TestUnitName]にディレクトリも追加する.

Testメソッドを実装

  • 以下が初期状態
<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class SampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}
  • 一番重要なのが assertHoge() のようなアサートメソッド. アサーションを行う.
    このメソッドがそのテストの成功失敗を判定する. 基本的に1メソッドに対して1アサートメソッドだが、複数指定も可能.
    (私は今回1:1でやったので複数指定の場合は他を参照してね.)

アサートメソッド一覧

アサートメソッドに関しては私がまとめるよりも公式が充実しているのでそちらを参照のこと.

DBとのやりとりが発生する場合の約束ごと

🙁💭 ここが一番大変だった、というか知らなかったのではまった.

  • 必ず setUp() すること
<?php
......
public function setUp()
{
    parent::setUp(); //おまじない. よくわからんがこれを書くことで環境をリセットしてる.
}

DBとの接続をするときに必ず必要.
各テストメソッドの実行前に呼ばれるメソッドで、テストメソッドごとに同じ環境を用意しテストメソッド内で動的に変化させて追従したい場合に使う.
DBとの接続で必要なのは、メソッドごとにDBの環境をリセットしたいから.
例)テストメソッド1で全データを削除→テストメソッド2で特定のデータを取得できることを確認すると、データが消えているのでNGとなる...ナド

  • tearDown() というのもあって、これは各テストメソッドの実行後に呼ばれる. あと処理が必要な場合はこれも実装が必要.

ざっくり書いてある記事があったので参考に:PHPUNITのテストメソッド実行前後の処理まとめ

DBとの接続(モデルにお任せバージョン)

🙁💭 どのサイトを見てもテスト内部でDBを指定して〜というのばかりで迷ったので書いておく.

<?php
......
/**
 * @test
 */
public function 情報を取得できる()
{
    //Nekoモデルインスタンスを作成し、factory1()で作成したデータをDBに登録している.
    $neko = factory(Neko::class)
                ->create($this->factory1()); //factoryについては後述
                
    $nekolist = $neko->selectAll(); //(modelのselectAll()で値を取得している.)
    
    TestCase::assertNotNull($nekolist); //帰ってきた値がNullでなければ"OK"
}

private function factory1()
{
    $data = ([
        'name'         => 'Tama',
        'kind'         => 'Abyssinian',
        'birthday' => '2022-02-22 22:22:22',
    ]);
        
    return $data;
}
  • @test アノテーションをつけることでメソッド名に"test"を入れなくてもテストメソッドだと判断をしてくれる
    アノテーションもたくさんの種類があるので公式サイトを見てみると良い. わたしは依存を示す @depends をそこそこ使った.

factoryとは?

モデルファクトリー:Laravel 5.1 テスト

テスト実行前に何件かのレコードをデータベースに挿入する必要はよく起きます。こうしたテストデータを手動でそれぞれのカラムへ値を指定する代わりに、Laravelでは「ファクトリー」を使用しEloquentモデルの各属性にデフォルトを設定できます。手始めにアプリケーションのdatabase/factories/ModelFactory.phpファイルを見てください。このファイルには最初からファクトリーの定義が含まれています。

🙂💬 複数のテストにまたがって、共通のいくつかのレコードをデータベースに登録したいときにファクトリーを呼び出すことで簡単にデータを設定することができる. (手動で特定の値を列ごとに設定しなくてよい)

以下ファクトリークラスの例

<?php
......
$factory->define(App\Neko::class, function (Faker\Generator $faker) {
    return [
        'name'         => $faker->name,
        'kind'         => $faker->kind,
        'birthday' => date('Y-m-d H:i:s'),
    ];
});
  • define()のはじめで対応するモデルを指定する.
  • $faker は呼び出し時にcreate()に渡される引数.
  • 渡した引数が定義に足りない場合は、定義の値がDBに登録される.

PHPUnitの実行

⚠️プロジェクトのルートにて実施すること

// テストの実行
$ vendor/bin/phpunit

実行結果

成功の場合

$ vendor/bin/phpunit
PHPUnit 6.4.0 by Sebastian Bergmann and contributors.

...............................................................  63 / 108 ( 58%)
.............................................                   108 / 108 (100%)

Time: 3.42 seconds, Memory: 24.00MB

OK (108 tests, 262 assertions)

エラーの場合以下のように出てきます.
. は成功、 F は失敗などいろいろある:PHPUnit マニュアル

$ vendor/bin/phpunit
PHPUnit 6.4.0 by Sebastian Bergmann and contributors.

....F..........................................................  63 / 108 ( 58%)
.............................................                   108 / 108 (100%)

Time: 2.87 seconds, Memory: 24.00MB

There was 1 failure:

1) Tests\Feature\NekoTest::ユーザ一情報覧を取得できる
Failed asserting that [エラー発生個所のフルパス] [エラーの内容]
...略...
~/tests/Feature/NekoTest.php:33

FAILURES!
Tests: 108, Assertions: 262, Failures: 1.

気づき

  • テストの対象にしたい機能をmodelで実装していないとテストにできないので、controllerへ依存しないようなコーディングが命.
  • このテストだけで全部を網羅しようと分割をやり過ぎても良くないので、必要な場面を判断しつつうまく活用するのが良い.
  • 求める結果がどのようなものかを理解していないと書けない.

わかってないところ

この部分は何???カバレッジ??? f:id:azix:20180525180747p:plain

信頼サイト

transaction処理中の動きで変数が更新されず嵌った.(解決済み)

これ、try-catch中の変数の変化を取得できてないのかな?と思い検証.

{{-- blade --}}

@if(session('message'))
    <ul class="list-group">
        <li class="list-group-item list-group-item-success">{{ session('message') }}</li>
    </ul>
@endif
//controller

//成功チェック
$success = false;

try
{
    DB::transaction(function () use ($success) {
        //登録処理
        DB::insert($data);
        $success = true;
    });
}
catch(Exception $e)
{
    $success = false;
}

//成功メッセージの表示
if($success)
{
    return redirect('cbt/user')
    ->with('message', '成功しました'); //ここが出力されない
}
else//TODO
{
    return redirect('cbt/user')
    ->with('message', '予期せぬ状態'); //ここが出力される
}


間違っていた箇所はフラグの変更位置.

 DB::transaction(function () {
        //登録処理
        DB::insert($data);
    });
    
    $success = true; //transaction外でフラグを変更.


どうやら、transaction内部ではDBでの処理が完了し次第、スコープの外に出てしまうようです.
以下のような処理にしても、returnしてくれなかった.

 DB::transaction(function () {
        //登録処理
        DB::insert($data);
        
        return redirect('cbt/user')
        ->with('message', 'これはtransaction内からのredirectです');
    });


まとめ

transactionメソッド中では、DB接続が終了し次第 exit 状態となるため、DB処理の後ろで何かやっても無意味!!!