エンジニアのひよこ_level10

毎日更新してた人。たまに記事書きます。

【PHP】油断すると破壊的な関数を書いちゃうよねって話【496日目】

こんなコードがありました

PHPに限った話ではないですが。

こんなコードがありました。

function changeUserStatus($user) {
    $user->status = 2
    return $user;
}

echo $user->status; // 1

$changed_user = $this->changeUserStatus($user);

echo $user->status;  //1. ??
echo $changed_user->status; //2. ??

さて。このとき1,2の値はどうなるでしょうか。

正解は

両方共2になりますね。

なぜか。インスタンスの参照先を渡して、インスタンスの値をそのまま書き換えてしまったから。

オブジェクトのインスタンスを扱うときは、結構やりがち。

どうしたらよかった?

もし、元の値を変更したくなかったのなら以下

function changeUserStatus($user) {
    //ここを書き換える
    $changed_user = clone($user);
    $changed_user->status = 2
    return $changed_user;
}

echo $user->status; // 1

$changed_user = $this->changeUserStatus($user);

echo $user->status;  // 1
echo $changed_user->status; // 2

clone()関数で全く同じものを生成して、それを書き換えてreturnする。

【Faker】boolean関数でTrueの確率を操作+元コード読む【489日目】

fakerって?

テストとかでよく使われるFaker。

ダミーの値を作るときによく使われていて、ランダム文字列、それっぽい電話番号、それっぽい住所などをランダム生成してくれます。

booleanの確率操作

trueやfalseを出す確率を実は操作できます。テスト内容によっては重要かも。

書き方は単純。引数に『True』が出る確率を書く。

$faker = new Faker\Generator();

$faker->boolean(50);

さて、元コードどうなってるんでしょう?

元コード

たぶん、これ。(ちょっと自信ないのが、Generatorのコメントから飛んだから)

vendor/fzaninotto/faker/src/Faker/Provider/Miscellaneous.php

    /**
     * Return a boolean, true or false.
     *
     * @param int $chanceOfGettingTrue Between 0 (always get false) and 100 (always get true)
     *
     * @return bool
     *
     * @example true
     */
    public static function boolean($chanceOfGettingTrue = 50)
    {
        return mt_rand(1, 100) <= $chanceOfGettingTrue;
    }

あーなるほどわかりやすい・・・

ココらへん読むと結構ニヤニヤできますよ(‘ω‘ )この機会に是非

【PHP】sprintfで同じ値を使いたいときに、コピペしない方法【479日目】

sprintfで同じ値が使いたい

sprintf('1. %s, 2. %s, 3. %d', 'apple', 'grape', 'apple');

こんなふうに、appleが被っている。

%dとかに数値を入れる

sprintf('1. %1$s, 2. %2$s, 3. %1$s', 'apple', 'grape');

これで、 %1$sには、appleが入り、 %2$sには grapeが入ります!

同じ値を使いたいってときは、とにかく値を並べるより、こっちの方がいいかもです!

【テスト】dataProviderで分岐で読み込む関数名をチェックする【439日目】

概要

XxxService

public function func($flag)
{
    if($flag) {
        return $this->user_repository->xxx();
    }

    return $this->user_repository->yyy();
}

こんなとき、xxxとyyyが読み込まれる場合をチェックしたい

コード

public testFunc($flag, $function_name)
    $mock = Mockery::mock('Tests\Unit\App\Stubs\StubUserRepository')->makePartial();

    // 該当関数が一度だけ呼ばれるのを期待する
    $mock->shouldReceive($function_name)
        ->once();

    (new XxxService($mock))->func($flag);
}

public function FuncDataProvider()
{
    return [
        'flagがtrueの場合' => [
            'flag' => true,
            'function_name' => 'xxx',
        ],
        'flagがfalseの場合' => [
            'flag' => false,
            'function_name' => 'yyy',
        ],
    ];
}

shouldReceiveの関数名の部分をdataProviderで分岐させてチェックする。

【PHP】配列で共通している値だけを抜き出す関数【437日目】

概要

1.以下のときに"apple","grape"の値だけを返したい

["apple", "orange", "grape"]
["apple", "grape", "banana"]

2.array_intersect()を使う

配列で、共通の値だけを抜き出したい

$array_1 = ["apple", "orange", "grape"];
$array_2 = ["apple", "grape", "banana"];

こんなときに、"apple","grape"を取り出したい。

ユーザーの共通項を取り出したり、
条件を配列で用意しておいて、条件一致しているものを確認するなどに使えますね。

array_intersect

$array_1 = ["apple", "orange", "grape"];
$array_2 = ["apple", "grape", "banana"];

return array_intersect($array_1, $array_2);

これでよし。

公式ドキュメント

PHP: array_intersect - Manual

【PHP】配列の中の空文字やnull等を削りたいときに使う関数【435日目】

概要

1.以下のときに"a"のキーだけを返したい

["a"=>"ringo", "b"=>null, "c"=>null, "d"=>false]

2.array_filter()を使う

配列で、特定条件のものを削りたい

["a"=>"ringo", "b"=>null, "c"=>null, "d"=>false];

こんなときに、"a"のキーのものを取り出したい。

nullとか、falseとか、想定外のものを削りたいとかよくありますよね。

array_filter

$array = ["a"=>"ringo", "b"=>null, "c"=>null, "d"=>false];

return array_filter($array)

これでよし。値がPHPの型変換でfalseになるものは全部削られる。0とかも削られる。
逆に言うと、trueになるものだけ残る。

となると、こういうこともしたくなる。

削るものを指定したい

削るものを指定したい。これも同じくarray_filterを使うことが出来る。

むしろ、それが本来の役割。第二引数に関数を指定するといい。

$array = ["a"=>"ringo", "b"=>null, "c"=>null, "d"=>false];

// ["a"=>"ringo", "d"=>false]
return array_filter($array, function ($value, $key) {
    return !is_null($value);
});

これで、nullはfalseになるので除外される。結果、trueになる"a"と"d"のキーのものだけが残った配列が返ってくる。

公式ドキュメント

PHP: array_filter - Manual

【Laravel】テスト時に、Sessionが持つ値を確認するassert【430日目】

セッションに値をもたせた

$request->session()->push('key', 'value');

こんなふうにセッションに値をもたせた時に、
その値を確認したい。

該当したキーと値のペアが存在するか確認したい

$this->assertSessionHas('key', 'value');

これでキーと値がチェックできる。

値を持っていないのを確認したい

$this->assertSessionMissing('key');

これでそもそもキーが存在しないのがわかる

公式ドキュメント

HTTPテスト 5.5 Laravel

【phpunit】同じテストコードをコピペしない工夫、dataProvider【423日目】

コピペでテスト書いてませんか

よくあるのが、入力を変えると、結果が変わるというテスト。

元コード

function testFunc($data)
{
....
}

テストコード

function testFunc_いろいろ返ってくる()
{
    $data = true;
    $expected = [1,2,3],

    .....


    $this->assertSame($expected, $result);
}


function testFunc_空っぽ()
{
    $data = false;
    $expected = [],

    .....


    $this->assertSame($expected, $result);
}

他コピペでdataとexpectedを変えていく・・・

こういうのもったいない。

入力と結果を関数で渡すdataProvider

こんなときに、$dataや$expectedを関数で渡してみよう

/**
  * @test
  * @dataProvider testDataProvider
  */
function testFunc($data, $expected)
{
    .....


    $this->assertSame($expected, $result);
}


function testDataProvider()
{
    return [
        'いろいろ返ってくる' => [
            'data' => true,
            'expected' => [1,2,3]
        ],
        '空っぽ' => [
            'data' => false,
            'expected' => []
        ]
    ];
}

何してるの?

テストのコメント欄に、 @dataProviderと関数名を書くと、
引数に値を入れることが出来る。

testDataProviderで配列を渡してやると、それをいい感じにテストしてくれる。
この場合、dataがtrueのときと、falseのときのテストが動く。

それぞれexpectedには、対応するdataが使われるので、見やすい!そしてコピペいらず!

ちなみに、キーに'空っぽ'とか日本語入れてますが、エラーが出たら、このキー名が表示されます。

めっちゃわかりやすい・・・!

参考になりそうなコード

github.com

クリックしたら、該当のコードに移ります。

ぜひdataProvider使って、コピペ減らして見やすいテストコードを!