RSpec基礎　

#RSpecの基礎について学んだので、アウトプットする

これからポートフォリオ作成に入るのだが、RSpecも理解した方がいいということで、
ドットインストール で学んだことをアウトプットする

（プログラミングノートにも同様の記述をする）

##概要
- 一言で言うと TDD を推し進めた BDD を実現するためのテストフレームワーク
- TDD が Test Driven Development テスト駆動開発　実際のコードを書く前にソフトウェアの要件を満たすテストを書きましょうという開発手法
- TDD をさらに推し進めて、ソフトウェアの振る舞いを記述することによってテストコードをより読みやすくした手法が BDD (Behavior Driven Development)

##公式サイト
http://rspec.info/

http://www.relishapp.com/rspec 

に詳細な仕様が記載されているので、こちらを参考にする

##必要知識
- Ruby
- UNIXコマンド

##TDD/BDD開発サイクル

1失敗するテストを書く　（red）
2最小限のコードを書いてテストを成功させる（green）
3リファクタリングをする（コードをきれいにする）

※rails チュートリアルで学んだことと同じ

##実際の作業

テストを書いてからコードを書いていくので 2 つのファイルが必要
そのほか設定ファイルがあったりすると便利

「rspec --init」コマンドで一気に作れる
spec/spec_helper.rb と .rspec の 2 つのファイルが作られる

```
$ rspec --init
```

.rspec というファイルは rspec コマンドにデフォルトで渡すコマンドオプション
何かオプションを追加したいときにはここに書いていけばいいので、覚えておく

####実際にテストを書くファイル
- 慣習的に spec フォルダに入れる
- ファイル名も慣習で「テストしたいクラスの名前_spec」とする　例）calc_spec.rb 

####実際のコードを書くファイル
- ライブラリという意味で lib フォルダを作って、その中にファイルコードを書く

##calc_spec.rbへの記述方法

- テスト対象となるものを「RSpec.describe "…" do end」と記載
- 実際にテストをする、どういった振る舞いを期待するかというのを「it "…" do end」に記載

```calc_spec.rb
RSpec.describe"A calc" do
	it "given 3 and 2,returns 5" do
	calc=Calc.new  #インスタンス作成
	expect(calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	end
end
```

```
$ rspec
```

と実行するとテストができる
この時点ではコードを書いていないので、失敗する

calc.rb を記述

```calc.rb
class Calc
	def add (a,b)　　#引数2つ
		5 #仮実装    なぜかというとテストコード（calc_spec.rb）の方にもし間違いがあった場合、calc.rb の方をいきなり実装していって複雑なコードにしてしまうと、どちらに間違いがあるかよくわからなるため、まずは結論だけ書く
	end
end
```

```
rspec
```

テストがパスするには、rspec側で読み込む必要がある

```calc_spec.rb

require 'calc'

RSpec.describe"A calc" do
	it "given 3 and 2,returns 5" do
	calc=Calc.new  #インスタンス作成
	expect(calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	end
end
```

これでテストはパスする
※F →.に変わるが.がパスした証となる

しかし、まだ不安がある。仮実装のため。なので2サイクル目を回す。

```calc_spec.rb

require 'calc'

RSpec.describe"A calc" do
	it "given 3 and 2,returns 5" do
	calc=Calc.new  #インスタンス作成
	expect(calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	end
	
	#三角測量
	it "given 8 and 5,returns 13" do
	calc=Calc.new  #インスタンス作成
	expect(calc.add(8,5)).to eq(13) #8+5は（to）13を期待する
	end
end
```

```
rspec
```

テストはパスしない。

```calc.rb
class Calc
	def add (a,b)　　#引数2つ
		5 #仮実装   
		a+b #明らかな実装
	end
end
```

```
rspec
```

テストがパスする

###リファクタリング　

テストコード内にcalc=Calc.new  が2つ存在するので、リファクタリングする


```calc_spec.rb

require 'calc'

RSpec.describe"A calc" do

	before do
		@calc=Calc.new  #またがって変数を指定する場合には、「@」を付けなくてはいけない
	end
	
	it "given 3 and 2,returns 5" do
	expect(@calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	end
	
	#三角測量
	it "given 8 and 5,returns 13" do
	expect(@calc.add(8,5)).to eq(13) #8+5は（to）13を期待する
	end
end
```

###describeについて
- describeは入れ子にしてパターンAの時、Bの時のように場合わけができる
- トップレベルの時はRSpec. ...と記載するのが推奨されているが、下位レベルでは推奨されていない。
- contextでも代用できる。テスト対象のものが物だった場合は「describe」、状況だった場合には「context」が使われる

```calc_spec.rb

require 'calc'

RSpec.describe"A calc" do  #トップレベル
	describe "when normal mode" do　　#下位レベル　descrobeで場合わけ
		it "given 3 and 2,returns 5" do
		expect(@calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	end
	
	describe "when graph mode" do
		it "draw graph" do
	end
end
```

###itについて
- itはテスト対象に期待する振る舞いを指定する
- itは「example」や「specify」で書き換えることも可能 使い分けは続く文章に対して、自然な文になるものを使えばOK（英語の文法の問題）
- it 後の"a calc"などの文章は省略可能　rspecで適当に文を作ってくれる
- do endも省略可能。その場合{}ブロックで指定する
-{}ブロックを書かない場合はペンディング（保留）となる。　期待する動きは明示したいけどテストはまだ書かない時などに使える


```calc_spec.rb

require 'calc'

#""省略
RSpec.describe Calc do
	it do  #省略可能　省略すると、rspecが適当に察して文章を作ってくれる
		calc=Calc.new  #インスタンス作成
		expect(calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	end
end

#do end省略
RSpec.describe Calc do
	it{
		calc=Calc.new  #インスタンス作成
		expect(calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	}
end

#{}ブロック省略
RSpec.describe Calc do
	it　"draw graph"   #pending
end
```


###matcherについて　　eqの部分
- 様々な検証が可能

```calc_spec.rb

		expect(@calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
		expect(@calc.add(2,3)).not_to eq(5) #2+3は（to）5以外を期待する
		expect(@calc.add(2,3)).to be true #trueを期待
		expect(@calc.add(2,3)).to be false #falseを期待
		expect(@calc.add(2,3)).to be >10 #2+3は10以上を期待
		expect(@calc.add(2,3)).to be_between(1,10) #1~10を期待
		expect(@calc.add(2,3)).to respond_to(:add) #addというメソッドがあるか調べる
		expect(@calc.add(2,3).integer?).to be true #整数であるかtrue #emptyとかもOK
		expect(@calc.add(2,3)).to be_integer #上の文と等価
}
```

そのほかにも matcher はたくさんあって、公式ドキュメントの「RSpec Expectations」のモジュールの「Built in matchers」に記載されている

https://github.com/rspec/rspec-expectations#built-in-matchers


###subjectについて
- describe のトップレベルでクラスを指定した場合、クラスのインスタンスを「subject」で代用することが可能
- 「calc = Calc.new」を書かなくても「expect(subject.add(2, 3)).to eq(5)」とするとまったく同じ動作になる
- テストが長くなってくると subject が何を指しているか分かりづらくなるので、次のような記法が推奨

```calc_spec.rb

require 'calc'

RSpec.describe Calc do
	#before {@calc=Calc.new}でも問題ないが、exampleの中では@変数を使わない方がいいと推奨されている
	subject(:calc){Calc.new}
	it {
	expect(@calc.add(2,3)).to eq(5) #2+3は（to）5を期待する
	}
end
```

###letについて
- 操作の主対象ではないけど変数で何かを管理したいという場合に使える

```calc_spec.rb

require 'calc'

RSpec.describe Calc do
	subject(:calc){Calc.new}
	#let
	context "tax5%" do
		let(:tax){0.05}
		it {expect(@calc.price(100,tax)).to eq(105) } #引数100×taxは105
	end
	context "tax8%" do
		let(:tax){0.08}
		it {expect(@calc.price(100,tax)).to eq(108) } #引数100×taxは108
	end
end
```

```calc.rb
class Calc
	def add (a,b)　　#引数2つ
		a+b #明らかな実装
	end
	def price(x,tax)
		x * (1+tax) #引数にtax*1を✖かける
	end
end
```

※その他注意

1. exampleごとに結果がキャッシュされる
　　example の中で tax を複数回呼んだ場合に、その回ごとに違った値が呼び出されるのではなくて、最初に評価された値が使い回される
2. 遅延評価される
	「let(:tax) { … }」は tax が出てきて初めてブロックが評価されるという仕組み　例

```calc_spec.rb
require 'calc'

RSpec.describe Calc do
	subject(:calc){Calc.new}
	let(:tax){   #taxを指定
		calx.tax=0.05
	}
	it {expect(tax).to eq(0.05) } #taxは0.05を期待
	it {expect(calc.price(100)).to eq(105) } #priceは105を期待
	end
end
```

```calc.rb
class Calc
	attr_accessor :tax
	def initialize
		@tax=0
	end
	def price(x)
		x * (1+tax) #引数にtax*1を✖かける
	end
end
```

この場合1つ目の`it {expect(tax).to eq(0.05) } `はパスするが
it {expect(calc.price(100)).to eq(105) } はパスしない。

2行目にはtax がどこにも入っていない。

let で書かれてあったとしても 1 回も実行されないため、calc.tax に 0.05 が入らなくて、105 を期待したけど 100 になってしまう

この遅延処理を防ぐ場合は、
`let!`とすることで、その場で評価される


###test double
- まだ実装していないけどテストをしたいというときに使える代役オブジェクト　英語のスタントマン＝stunt doubleから由来
- いくつか種類がある

####method doubleについて

データベースに接続してそれを引っ張ってきて…、ということをやりたいけどそこまでデータベース関連の実装が進んでいない、だけどテストがしたいという場合

```calc_spec.rb
require 'calc'

RSpec.describe Calc do
	it {
		user=double('user')#引数は書かなくてもOK だが書くとエラー時にいろいろ教えてくれるらしい
		allow(user).to receive(:name).and_return('taguchi')#userに対してnameメソッドを無理つける、必ず値を「taguchi」で返せと指定
		#user.name　-> taguchi nameの中身がなくても、user.nameが実行できる　taguchiという値が必ず返る
		calc=Calc.new
		expect(calc.add(5,2,user.name)).to eq('7 by taguchi')
	}
end
```

```calc.rb
class Calc
	def add(a,b,name)
		(a+b).to_s+'by'+name
	end
end
```

####message expectation
- 呼び出されなかったらテストが失敗するというもの 呼ばれなかったらfailになる
- ある処理の前に確実にある処理が走っていることを保証したいときに使う


```calc_spec.rb
require 'calc'

RSpec.describe Calc do
	it{
		logger = double('logger')   #logger を test double で作る
		expect(logger).to receive(:log)  #logger が必ず log というメソッドを呼ばなくてはいけないと記述
		calc = Calc.new(logger)         #logger は Calc に渡さないといけない
		expect(calc.add(5,2)).to eq(7)
	}
end
```

```calc.rb
class Calc

	def initialize(logger)   #initializeするときにloggerを受け取る　この状態だとloggerのlogを呼ばないとエラーになる
		@logger=logger
	end
	
	def add(a,b)
		@logger.log   #logメソッドは定義してないが、logを呼ばないとエラーになる
		a+b
	end
end
```

何らかの処理の前に必ず何らかの処理を呼びたい場合に使う


###example を共有する方法
- テストをまとめて変数のようにすることができる

例えば Calc クラスに normal mode と expert mode があって、それぞれにテストを書きたい場合

```calc_spec.rb
require 'calc'

RSpec.shared_examples "basic functions" do   #変数のようにまとめておける
	it "can add"
	it "can subtrace"
	it "can multiply"
	it "can divide"

RSpec.describe Calc do
	content "normal mode" do
		include_examples "basic functions"   #呼び出す
	end
	content "normal mode" do
		include_examples "basic functions"  #呼び出す
		it "can draw graph"
	end
end
```

以上