ワンライナーなspecを書きやすくするrspec-givenをもっといい感じに使う

# もっといい感じにspecを書きたい

以前に[rspec-givenについてこのような記事](http://qiita.com/kakkunpakkun/items/7b9c5a463cc7d0ba67a6)を書いたんですが、それなりに長い期間使っててより良さ気な使い方や、勘違いだったことなどもあり、もっといい感じに使うならこうしようと考えたものを書いておきます。

rspec-givenの紹介についてはこちらをお読みください。
[rspec-givenですっきりしたspecを書く](http://qiita.com/kakkunpakkun/items/7b9c5a463cc7d0ba67a6)

以下、ところどころ[rspec-given](https://github.com/rspec-given/rspec-given)のドキュメントから引用しつつ書いていきます。

# Givenはletと同じように使う
例えばこういうコードがあった場合について

```rb
Given(:stack) { Stack.new }
```
> The block for the given clause is lazily run and its value bound to 'stack' if 'stack' is ever referenced in the test. 

こうある通り、要はletと同じ動きです。
遅延評価をするので呼ばれるまでブロックは実行されません。

これは何かのインスタンスメソッドについて書く時に重宝します。
多少雑なspecですがこういう風にarrayを渡すのに使いやすいです。

```rb
describe '#pop' do
  # array.popについてテストする
  subject { array.pop }

  # arrayがこういうものの場合
  Given(:array) { [1, 2, 3] }

  # 結果はこうなる
  Then { subject == 3 }
end
```

なので、rspec-givenを使ってるならletは使わないようにしています。
同じ結果同じ意図で、違う記法のものはない方がいいかと思うので。


## Given!は使わない

Given!については
> The code block is run unconditionally once before each test

というように書いてあるのですが、結局動作がWhenと同じになってしまうので特に理由がない限りは使わない方が良いと思います。いくつもの記法があると混乱のもとになりますし。
Whenを使うようにしましょう。


# Whenはbefore(:each)みたいなもの

> The code block is executed once per test.

基本的に`before(:each)`と同じように動き同じタイミングで実行されます。
beforeと違うのは`When(:company) { create(:company) }`のように書けるので、`company`をテストの中で使えたりすることです。

beforeのように`@company`などとインスタンス変数を作らなくてもいいのは読みやすくなってとても良いです。

ただ、Whenは上から実行していくのでWhenを大量に書くとどれが実行されたか分かりにくくなりやすいです。これは`before(:each)`がたくさんあるspecが読みにくいのと同じ理由ですね。

`Given { ... }`と書いてもWhenと同様に各テスト毎に動くんですが特に使う理由はない気がしてます。

Whenはcontextと組み合わせて「必ず実行したい条件」を書くときれいになりやすいです。

```rb
describe '#pop' do
  subject { stack.pop }

  context 'when stack is not empty' do
    When(:stack) { [1, 2, 3] }
    Then { subject == 3 }
  end

  context 'when stack is empty' do
    When(:stack) { [] }
    Then { subject == nil }
  end
end
```
describeで何をテストしようとしているのかを示し、contextで条件を示して、その条件をWhenで実装する感じです。


## GivenとWhenのタイミング

要はGivenは呼ばれた時に評価、WhenはThen実行直前に評価されると思っておくと良いと思います。
その意識で読みやすくするためにも`Given {...}`は使わず、`When {...}`を使った方が良さそうです。
Givenは`Given(:result) {...}`と、遅延評価を使えるように使いましょう。

## よりきれいなWhenの使い方

理想は一つのcontextに一つのWhenがあるように書くことで、そうするとカバレッジも高くしやすいですし、なにより読みやすくなります。

なので`before(:each)`も特別な理由がない限り使わないようにしています。


# subjectと併用
rspec-givenのサンプルではGivenやWhenの結果をThenでテストしてたりしますがそうすると
「今なにについてテストを書いているのか」
が読む人にとっても書いてる人にとっても分かりにくくなるので普通にrspecのsubjectを使うと分かりやすいと思ってます。

特に普段からrspecでsubjectを使っていた場合は読みやすいかなと思います。

```rb
describe Stack do
  def stack_with(initial_contents)
    stack = Stack.new
    initial_contents.each do |item| stack.push(item) end
    stack
  end

  # このstackについてテストを書いているように見えにくい
  Given(:stack) { stack_with(initial_contents) }
  
  context "with no items" do
    Given(:initial_contents) { [] }
    Then { stack.depth == 0 } # ん？stackってどこで定義したっけってなりやすい
  end
end
```

下のようにsubjectを使う

```rb
describe Stack do
  def stack_with(initial_contents)
    stack = Stack.new
    initial_contents.each do |item| stack.push(item) end
    stack
  end

  # これがここでテストしたい対象だと分かるようにする
  subject { stack_with(initial_contents) }
  
  context "with no items" do
    Given(:initial_contents) { [] }
    Then { subject.depth == 0 } # subjectということはsubject句があるんだなと思いやすい
  end
end
```

これはrspecへの慣れ具合にもよるかもしれません。

# itは使わずにThenを使う

ThenはNatural Assertionが使えるitだと思って良いと思います。

Natural Assertionについては[rspec-givenのドキュメント](https://github.com/rspec-given/rspec-given)を読むか私が[以前書いたNatural Assertionについて](http://qiita.com/kakkunpakkun/items/7b9c5a463cc7d0ba67a6#1-4)を参照ください。

Thenはitと同じタイミングで実行されます。
なのでitは使わず、Thenを使うようにしています。


# AndはThen内での評価を使いまわす

AndはThenに続けて使います。
例えばこのようになります

```rb
# userをDBに保存する場合
Given(:user) { User.create(name: name) }
Given(:name) { 'kakkunpakkun' }

Then { user.persisted? == true } # ここでuserが叩かれたときにDBに保存する
And  { user.name == name } # ここでは新たに保存をせず、Thenで作られたuserを使う

Then { user.class == User } # ここでは新たにuserをDBに保存する
```

RSpec2まであったitsのように一つのインスタンスのattributesについてチェックしていきたい場合にムダな処理が走らなくて良いです。


# 公式のリポジトリはそれじゃない

ちょっと番外編

rspec-givenで検索するとこういう結果になりますが、それはrspec-givenの公式リポジトリじゃない！

![スクリーンショット 2015-03-07 18.35.04.png](https://qiita-image-store.s3.amazonaws.com/0/8772/cc159077-fecf-6e38-81d5-bf1e3c4d68f9.png)

以前はjimweirichさんの個人リポジトリが公式でしたが、今はrspec-given/rspec-givenがあります。
普通に検索するとだいぶメンテされてないライブラリに見えますが、公式はちゃんとアップデートされてるのでご安心を

https://github.com/rspec-given/rspec-given