type holyshared = Engineer<mixed>

技術的なことなど色々

Expectationを再設計した話。

expectationを開発していて、ちょっと気に入らない箇所がいくつか出てきたので、再設計してみた。

再設計したので、expectationはもうメンテしておらず、 expectに変わったので、注意してください。 また、peridotプラグインも、peridot-expect-pluginに変わり、再設計した方のものに変わっています。

再設計後のマッチャーAPIは互換性担保しているはず。
設計を見直した点は、下記の通り。

  1. アノテーションを使用するメリットがそんなになかった。

    旧ロジックはアノテーションでMatcherのどのメソッドを使用して、評価するかを指定する設計だった。 こんな感じで、アノテーションで指定する。

     ```php
     /**
      * @Lookup(name="toEqual")
      * @Lookup(name="toBe")
      * @param mixed $actual
      */
     public function match($actual)
     {
         $this->setActualValue($actual);
         return $this->getExpectValue() === $this->getActualValue();
     }
    
     /**
      * @Lookup(name="toBeTrue")
      */
     public function matchTrue($actual)
     {
         return $this->setExpectValue(true)->match($actual);
     }
     ```
    

    アノテーションエイリアス割り当てられるくらいのメリットぐらいしかなかったので、使わないようにした。

  2. Matcherのソースコードに対する、対応付けがわかりににくい。

    Matcherをソースコードと1対1になるようにした。

    • 例: toBeTrue -> ToBeTrue.php
    • 例: toBeEqual -> ToBeEqual.php
  3. エラーメッセージがわかりにくい。

    rspec-expectationsgomegaあたりが、どうしているか参考にした。

Peridotのプラグインを使用していた人へ

移行はそんな難しくなくて、peridot.phpを編集して、ExpectationPluginの部分をExpectPluginにします。 基本的にはこれでOKなはず。

use expect\peridot\ExpectPlugin;

return function(EventEmitterInterface $emitter) {
    ExpectPlugin::create()->registerTo($emitter);
};

あとはspec書くだけ。

describe('Example', function() {
    describe('#create', function() {
        it('return new Example instance', function() {
            expect(Example::create())->toBeAnInstanceOf('Example');
        });
    });
});

まとめ

いつから俺は、be_truthy/be_falseyの仕様を勘違いしていたのだ

PHPexpectationライブラリ作っているのですが、思いっきり間違った実装をしていることに、rspecのリファレンスを見ていて、気がつきました。

matcherのtoBeTruthy/toBeFalseyが、rspecのmatcherでは、

  • be_truthy - nil、若しくはfalseでない
  • be_falsey - nil、若しくはfalse

なんですね。 自分はずっと、be_true / be_falseのエイリアスだと思っていた...。

リファレンスをみるとちゃんと書いてある。

    expect(actual).to be_truthy    # passes if actual is truthy (not nil or false)
    expect(actual).to be_falsey    # passes if actual is falsy (nil or false)

というわけで、バージョン1.4.3で修正しました。
古いバージョンでは、toBeTruthy/toBeFalseyが、toBeTrue/toBeFalseと等価の振る舞いになっています。