let, インスタンス変数, ローカル変数, 定数 in RSpec

注意: 詳細な話は
[RSpecのletを使うのはどんなときか？（翻訳）](http://qiita.com/jnchito/items/cdd9eef2ed193267c651)
と
[「RSpec で example の外で定義したローカル変数を使うのはアリか？」に対する僕の見解と解決策](http://blog.jnito.com/entry/2016/09/03/145148)
あたりを見てもらう方が早いです

別に目新しい何かを語るわけではないので結論から話すとletでいいぞっていう話です。

## 概要
レビュー時に「この変数をletで定義する意味がないと思う」というレビューをもらった。

```
describe 'test' do
  let :user_name do 'test' end
  let :password do 'password' end

  it 'username' do
    expect(User.new(user_name, password).user_name).to eq('test')
  end

  it 'password' do
    expect(User.new(user_name, password).password).to eq('password')
  end
end
```

とりあえず共通化できる変数はまとめてちゃえの精神の元,宣言していたのでいまいちletを使う
理由というを即答えることができなかった。
この規模のテストならこうもかけるし

```
describe 'test' do
  user_name='test'
  password='password'

  it 'username' do
    expect(User.new(user_name, password).user_name).to eq('test')
  end

  it 'password' do
    expect(User.new(user_name, password).password).to eq('password')
  end
end
```

こうもかける

```
describe 'test' do
  # eachでもいいしallでも良い
  before(:each)do
    @user_name='test'
    @password='password'
  end

  it 'username' do
    expect(User.new(@user_name, @password).user_name).to eq('test')
  end

  it 'password' do
    expect(User.new(@user_name, @password).password).to eq('password')
  end
end
```

~~正直動くしどれでもいい、脳死でletでええやんけ~~
さぁどれが良いのだろうか。

### letの場合の挙動
letのドキュメントは[これ](https://relishapp.com/rspec/rspec-core/docs/helper-methods/let-and-let)かな?
ざっくりいうと「同じexample内は最初に呼ばれた値をキャッシュして使い回すよ。でも違うexampleの場合また呼び直すよ。(で同じexmaple内ではそれを使い回す)」
すなわちbefore(:each)的な動きをする。
当然it内で色々letの値をこねくり回して放置しても次のit内では新しくletないのblockで初期されるって寸法なわけだ。話の分かるやつだ。

### インスタンス変数(before block)

beforeブロックで定義できる。

```
describe 'test' do
  @test = true
  it 'test' do
     expect(@test).to eq(true)
  end
end
```
こういうのはあかんみたい。　いまいちインスタンス変数のスコープがわからん

before構文は色々種類があるのでどれで初期化するのかで挙動が変わるけど

```
describe 'test' do
  before do
    test = true
  end

  it 'test' do
    expect(@test).to eq(true)
  end
end
```
こういう感じでローカル変数を定義するのはできないようなのでbefore blockで変数を定義して、it内で利用するならインスタンス変数しかないのかな。

これでもいいという人は多そうだけど、<b>なんかなんでもかんでも@つけるのダサくないですか?</b>
後、@をつけて回るのめんどくさいです。

### ローカル変数
まぁ動くけど下の例のような場合ちょっと怖い

```
describe 'test' do
  test = true
  it 'test' do
     expect(test).to eq(true)
　　　　　　　　　　test = false
  end
  
  context 'true' do
    it 'test' do
      expect(test).to eq(true)
    end
  end
end
```

要は意図しない形で変数が書き換えられた時対応が難しいという問題がある。

じゃぁ定数?

```
describe 'test' do
  test = true.freeze
  it 'test' do
     expect(test).to eq(true)
　　　　　　　　　　test = false
  end
...
```

warningは出るけど再代入はできるのでちょっと怖いね。
つまりローカル変数を使用するようなケースは複数のexampleやitブロック間で共有する変数を使用したくてなおかつインスタンス変数やグローバル変数を使用したくない場合?
~~クソテスt~~あまり考えたくないケースですね(棒)

### letを使用する理由
基本的にletでやっとけば変数は安全に初期化されるし、定数(freeze)よりも安全性が高いのではないかという気がしてきたが他にletを利用した方が良い理由としては以下の点が挙げられる。

* typoにすぐ気づける (代入文でない限りtypoした場合「undefined local variable or method」のエラーがでる。インスタンス変数の場合nilと評価される)
* ローカル変数からすぐ変更できる。(インスタンス変数ではないので@とかつける必要ない)
* 無駄な初期化が防げる(遅延評価なので使われない場合初期化処理は実施されない)
* かっこいい!(ブロックはかっこいい！)

上記理由は
[RSpecのletを使うのはどんなときか？（翻訳）](http://qiita.com/jnchito/items/cdd9eef2ed193267c651)
にありますので詳しくはそちらを参考にしてください。

定数だろうがなんだろうがletで定義しときゃよしなにしてくれるって！
当然遅延評価なので、letで初期化した結果が他の場所で影響してくる場合注意する必要が出てくるのかな?

#### 遅延評価が原因でテストが失敗する例

```
describe 'test' do
  let :test do @test = 'test'; 'test' end

  it 'test' do
    expect(@test).to eq('test')
  end
end
```
上の例が遅延評価が原因でテストが失敗する例
実用性?犬にでも食わせておけ。

```
Failures:

  1) test test
     Failure/Error: expect(@test).to eq('test')

       expected: "test"
            got: nil
```

``` @test ```がexpect時に宣言されていないのでnilになってしまう。

すぐに評価してほしい場合は

```
describe 'test' do
  let! :test do @test = 'test' end
...
```
上のようにlet!を使用しよう。その場合どうも挙動を見る限りit,exampleが実行される直前にlet block内が評価されているように見える。(つまり無駄な初期化が発生する)

ざっと調べた感じがこんな感じ。

楽しいtest lifeを