Given-When-Then

BDD 云々について調べてたら Google グループ を見つけて、んでまぁチラチラと過去ログ読んでたら GWT という(考え方の)フレームワークがあった。

  • Given: ○○な(状態|状況)で
  • When: ××をしたら
  • Then: △△(が起きる|になる)

ちょっと Given というのがわかりにくかったんだけど、たぶん肝は a) ×× というキッカケ と b) ○○ という状況 を区別する、というところ。英語的には、b) の方も "when" で書けちゃうってのがもにょもにょする。

  • Given: 酔っ払ってるときに
    • When: クルマを運転すると
      • Then: 事故る

で、実装するのがめんどくなりそうな話なんだけど、Given と When は

  • 入れ子にできる
  • 順番は問わない

とした方が自然に書ける。書けるって、ストーリーを。というのは、ある程度の状況がすでに決まってる状態で、追加の Given: □□だったら というのを思いつきやすい、ようだ。

  • Given: 酔っ払ってるときに
    • When: クルマを運転すると
      • Given: お巡りさんがいたら
        • Then: タイーホ
      • Given: お巡りさんがいなかったら
        • Then: 事故る

なにも考えないで S-Expression にすると

(given ...
  (when ...
    (given ...
      (then ...))
    (given ...
      (then ...))))

when って困るんですけど!

プログラム的には、Java とか C# みたいな言語だと対象の object の状態によって挙動が変わるということがしょっちゅうあるんだろうけど、lisp だとあんまりそーゆーことしない雰囲気なんで、Given はあんまいらないかなーと思ったり。でも、when だけ使えるように実装してあとから given も使えるようにって変更はめんどそうなんで、やるなら一気にやっちまいたい。

レポートをどうすりゃいいもんかなーとも思ったり。

Given: 酔っ払ってるときに
  When: クルマを運転すると
    Given: お巡りさんがいたら
        [FAILURE]
        Expected: タイーホ
        Actual: 事故った

前に考えてた (expect EXAMPLE-FORM EXPECTATIONs) だと EXAMPLE-FORM と EXPECTATIONs が1対1で対応してたんだけど、そーゆーわけにいかなくなる。fail とか pass とかいうのは、そこまでに積み重ねられた Given と When の先に Then があって、その Then に付随する。

飲酒運転の例だと、こう出力できればベストなんだろうとは思う。

酔っ払ってるときに車を運転すると
  お巡りさんがいたら
      [FAILURE]
      Expected: タイーホ
      Actual: 事故った
  お巡りさんがいなかったら
      事故った


Then の更に子(というか続き)でまた Then とかが書きたくなる場合もありそう。

  • Given: お巡りさんがいたら
    • Then: タイーホ
      • Given: 会社員だったら
        • Then: 懲戒免職
      • Given: 芸能人だったら
        • Then: 謝罪会見を開く

えーと、Then がその後では Given(状況)になると解釈すればいいんだろうか。でも書いてある順番は時間的に前後とかっていうわけではないんだよなぁ。

(given ((alist '((foo . 1) (bar . 2) (baz . 3))))
  "alist が ((foo . 1) (bar . 2) (baz . 3)) という list のとき"
  (when (nalist-to-plist alist)
    "nalist-to-plist に alist を渡すと"
    (then
      (it-returns "(foo 1 bar 2 baz 3) という plist を返す"
        '(foo 1 bar 2 baz 3)))))

(given ((plist '(foo 1 bar 2 baz 3)))
  "plist が (foo 1 bar 2 baz 3) という plist のとき"
  (when (nplist-to-alist plist)
    "nplist-to-alist に plist を与えると"
    (then
      (it-returns "((foo . 1) (bar . 2) (baz . 3)) という alist を返す"
        '((foo . 1) (bar . 2) (baz . 3))))))
alist が ((foo . 1) (bar . 2) (baz . 3)) という list のとき
  nalist-to-plist に alist を渡すと
  > (nalist-to-plist alist)
      [FAILED!!] (foo 1 bar 2 baz 3) という plist を返す
      - Expected Return: (foo 1 bar 2 baz 3)
      - Actually Returned: (foo bar baz)

plist が (foo 1 bar 2 baz 3) という plist のとき
  nplist-to-alist に plist を与えると
  > (nplist-to-alist plist)
      [FAILED!!] ((foo . 1) (bar . 2) (baz . 3)) という alist を返す
      - Expected Returns: ((foo . 1) (bar . 2) (baz . 3))
      - Actually Returned:
      - Actually Signaled: type-error "不正なデータ型です: nil: cons"