PHPUnit のモックで、引数に応じて戻り値を変更する #php #PHPUnit

概要

  • returnValueMap を使うと引数に応じて戻り値を変更できる
  • 例外を返したいときは returnCallback() を使うと便利

returnValueMap を使う

returnValue に [引数, 戻り値] の配列を渡すと、引数に応じた戻り値を返す Mock を作れる。

引数が複数ある場合は、 [引数1, 引数2, 戻り値] のような形式にしてあげればよい。

// Mock を作成
$mock = $this->createMock(HogeClass::class);

// returnValueMap に [引数, 戻り値] の配列を渡す
$mock->method('doSomething')
      ->will($this->returnValueMap(
        [
          ['arg1', 'ret1'],
          ['arg2', 'ret2'],
        ]
      ));

// 引数に応じた値が帰ってくる
$this->assertSame('ret1', $mock->doSomething('arg1'));
$this->assertSame('ret2', $mock->doSomething('arg2'));

例外を返す場合は returnCallback を使うとよさそう

returnValueMap だと通常の戻り値と例外のケースを同時に同じ Mock ができなそう。

返り値を callback にできる returnCallback を応用して実現してみる。

$mock->method('doSomething')
      ->will($this->returnCallback(
        // doSomething が実行されると、callback が呼ばれる
        function ($arg) {
          if ($arg === 'arg1') {
            return 'ret1';
          } elseif ($arg === 'arg2') {
            return 'ret2';
          } elseif ($arg === 'invalid') {
            // 引数によって例外を投げる
            throw new InvalidArgumentException('doSomething called by invalid argumnet. Input was: '.$arg);
          }

          return 'retDefault';
        }
      ));

引数と返り値を切り出す

returnCallback でやりたいことは実現できたのだが、分岐を追記していくスタイルは辛いので、処理を切り分ける。

// 引数と戻り値の紐付けを切り出す
$map = [
  'arg1' => function () { return 'ret1'; }],
  'arg2' => function () { return 'ret1'; }],
  'invalid' => function () { throw new InvalidArgumentException('doSomething called by invalid argumnet. Input was: '.$arg); }],
  'default' => function () { return 'retDefault'; }],
];

$mock->method('doSomething')
      ->will($this->returnCallback(
        function ($arg) {
          if ($map[$arg]) {
            return $map[$arg]();
          }

          return $map['default']();
        }
      ));

補足

そもそも Mock に色々やらせすぎたり、 Mock を使いすぎたりするのは微妙だと思う。

ただ Mock をケースごとに差し替えるのは大変なときにはこういうやり方が使えそう。

参考

課題に正面から取り組むこと

経験を積むと、実現が難しい方法で苦しんでるときに、より容易な回避方法が浮かぶようになる。

手元に並べられる選択肢が増えるというか。

それはいいことなんだけど、「より最適な解として」別の手段を選んでるつもりで、「難しい方法を避けるために」別の手段を選んではいないか。

特に「納期」みたいなものは分かりやすく、そして言い訳にしやすい。

「今回はスケジュールが厳しいからこの方法にしよう」

安全策は最後のライフラインとして傍らに確保しつつ、正面から立ち向かう気持ちを忘れずにいたい。

私は弱い人間で、すぐ逃げようとするので。

DataGrip で クリアテキスト認証 を使う #DataGrip

概要

IAM 認証 による RDS 接続をする際などに、 MySQL のクリアテキスト認証が必要になる。

DataGrip から利用する方法をまとめておく。

おそらく Jetbrains 製品の Database Viewer でも同じ方法でできると思う。

参考: IAM認証によるRDS接続を試してみた | Developers.IO

設定方法

  • DataSource の設定項目の Advanced タブを開く
  • authenticationPlugin に com.mysql.cj.protocol.a.authentication.MysqlClearPasswordPlugin を指定する
  • VM environment に LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN=1 を設定する

ドライバーに MySQL for 5.1 を利用している場合

パッケージ名が異なるので、 com.mysql.jdbc.authentication.MysqlClearPasswordPlugin を指定する。

参考

IntelliJ IDEA から MySQL に繋ごうとしたら Communications link failure #IntelliJIDEA

問題

IntelliJ IDEA や DataGrip から MySQL に接続したら、エラーになってしまった。

接続テストでは成功するのに、実際に繋ごうとすると Communications link failure と言われてしまう。

java.io.IOException: Socket is closed. Communications link failure. The last packet successfully received from the server was 24 milliseconds ago. The last packet sent successfully to the server was 30 milliseconds ago.

対応

接続設定の Driver を MySQL から MySQL for 5.1 に変更すると解消した。

接続先の MySQL は 5.6 だったんだけど、ドライバーのバージョンとの相性が原因だろうか。

f:id:su-kun1899:20200226105313p:plain

参考

1207. Unique Number of Occurrences

LeetCode の挑戦ログ

Problem

https://leetcode.com/problems/unique-number-of-occurrences/

  • 整数の配列を渡される
  • 値の登場回数がユニークで構成される場合は true を返す

Solution

class Solution {
    public boolean uniqueOccurrences(int[] arr) {
        Map<Integer, Long> map = Arrays.stream(arr)
                .boxed()
                .collect(Collectors.groupingBy(i -> i, Collectors.counting()));

        int valueCount = map.size();
        long occurrenceCount = map.values().stream().distinct().count();

        return valueCount == occurrenceCount;
    }
}

Impressions

  • 要素の数を数えたいときは、 Collectors#groupingBy の第 2 引数に Collectors.counting() を渡す

References

Measure What Matters を読んだ

巷で話題の OKR の本ということで読んでみた。

チームがチームとして機能するためには、同じ目標にフォーカスすることが大切だと思うんだけど、個々人の見ているものは想像以上にブレやすいというのは常々感じている。

なので明文化して常に立ち帰れるようにしておくのがいい。

プロジェクトベースだとインセプションデッキとか ODSC とかがあるけど、 OKR はもっと上位の、経営戦略みたいなものから紐付けてツリーを作っていくイメージ。

もちろん OKR が全てを解決するわけではないけど、いい仕組みだなとは思った。

本の中では基本 OKR 絶賛なわけだけど、ちゃんと失敗例も紹介している。

目標設定ってやつが大嫌いなんだけど、OKR ならやりたいかな。

少し気になるところとすれば、仮に明文化されていたとしても、それを定着させ、フォーカスがずれてきた時にアナウンスしていくことがキーになるということ。

チームでそれができればいいんだろうけど、体感としてはそれは難しくて(少なくともチームがスキルとして身につける必要があって)、できない時は役割として担う人が必要になると思う。

それをマネージャーと呼ぶならそうなのかもしれない。

あと、人事評価と紐付けないこと自体はいいと思うんだけど、個人の目標設定にまで紐づける必要があるのかは微妙だと思う。

せいぜいスモールチームまでじゃないかなぁ。

人材戦略みたいなものが明確に定義されてるならそれに紐づく OKR を個人が持つこともあるのかもしれないけど。

それは「個人での達成よりチームでの達成の方が好き」という僕自身の価値観ゆえの印象といえばそれは否めない。

977. Squares of a Sorted Array

LeetCode の挑戦ログ

Problem

https://leetcode.com/problems/squares-of-a-sorted-array/

  • 整数の昇順でソートされた配列が渡される
  • 各要素を自乗して、それを昇順でソートした配列にする

Solution

class Solution {
    public int[] sortedSquares(int[] A) {
        return Arrays.stream(A)
                .map(i -> i * i)
                .boxed()
                .sorted()
                .mapToInt(i -> i)
                .toArray();
    }
}

Impressions

  • ボクシングするのは毎度ながらめんどい
  • 累乗には Math#pow がある
    • double であつかう IF しかない
    • 今回は整数というのが分かっていたので使わなかった