#はじめに
RSpecの基本的な勉強をしたので学習をまとめる
#RSpecとは
公式ドキュメントの翻訳
RSpec は、Ruby プログラマー向けのビヘイビア駆動開発ツールです。BDD は、テスト駆動開発、ドメイン駆動設計、受け入れテスト駆動計画を組み合わせたソフトウェア開発手法です。
これだけでは分からなかったので、他を参考にさせてもらうとテストフレームワークの1つ、ということみたい。
#メリット
自然言語(英語等)を併記するコードの特徴から可読性が上がる
TDDの観点からだとコードの変更時に予期せぬ不具合を防げる
#導入手順(Ruby)
プロジェクトの下準備
# ディレクトリの作成
mkdir project_ruby # プロジェクトファイル
mkdir project_ruby/lib # テスト対象のファイルを入れるディレクトリ
# ディレクトリに移動
cd project_ruby
# bundleの初期化(Gemfile作成)
bundle init
# gemの追加とインストールを同時に行う
bundle add rspec
# RSpecの設定ファイルを作成(.rspec, spec/spec_helper.rb)
bundle exec rspec --init
次に読み込みの設定を行う。
#lib内のファイルを全て読み込むように設定する時
Dir[File.join(File.dirname(__FILE__), "../lib/**/*.rb")].each { |f| require f }
#特定のファイルを読み込む設定にする時
require_relative "../lib/読み込みたいファイル名.rb"
読み込み自体はテストコードに直接書いても挙動は変わらないようなので、共通設定はspec_helper.rb
に書く方が重複せず済む。
これで準備完了。
lib
にテスト対象のコードを書く
spec
にテストコードを書く
ファイル | 説明 |
---|---|
lib | テスト対象をまとめるライブラリ(ディレクトリ) |
.rspec | RSpec用の設定ファイル |
spec | RSpec用のディレクトリ |
spec_helper.rb | RSpecの動きをカスタマイズするファイル |
導入手順(Rails)
プロジェクトの下準備
# プロジェクトの作成、postgresql指定とtestディレクトリを除外
rails new project_rails -d postgresql -T
# プロジェクトへ移動
cd project_rails
# DBの作成
rails db:create
Minitestを削除する明確な理由を見かけなかったが、そもそも格納するテストコードは別々のディレクトリ構造なので、必要ないから削除しているぽい。
続いて、Gemfileにgemを追加
#Rails用のフレームワークを追加
group :development, :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'rspec-rails'
end
最後にgemのインストールと設定ファイルの作成
# gemのインストール
bundle install
# RSpec用の設定ファイルの作成
rails g rspec:install
これで完了。
設定ファイルはRubyの時とは異なり、rails_helper.rb
が追加されている。
ファイル | 説明 |
---|---|
.rspec | RSpec用の設定ファイル |
spec | RSpec用のディレクトリ |
spec/spec_helper.rb | RSpecの動きをカスタマイズするファイル |
spec/rails_helper.rb | RSpecの動きをカスタマイズするファイル |
#テストの実行
# 全てのRSpecを実行
bundle exec rspec
# 一部のRSpecを実行
bundle exec rspec spec/実行したいファイル.rb
#実行結果
=> No examples found.
=> Finished in 0.00052 seconds (files took 0.33436 seconds to load)
=> 0 examples, 0 failures
実行結果は下記の通り。
実行結果 | 説明 |
---|---|
No example | 実行結果の内容(記述していないので今回は何も表示されいない) |
Finished | テストにかかった時間を表す |
0 examples | 「0個のテストを実行した」を表す。itの数に比例。 |
0 failures | 「0個のテストを失敗した」を表す。 |
0 pending | 保留マークつけた箇所の警告を表す(上記にはない) |
#失敗した実行結果
下記がテストの対象
#テスト対象
class Student
attr_reader :name, :age
def initialize(name:, age:)
@name = name
@age = age
end
def introduce
"私は#{@name}です。年齢は#{@age}才です。"
end
end
下記がテストコードとした時
#テストコード
RSpec.describe Student do
describe "initialize" do
context "インスタンスが生成された時" do
it "名前の値が正しいこと" do
student = Student.new(name: "suzuki", age: 20)
expect(student.name).to eq "suzuki"
end
it "年齢の値が正しいこと" do
student = Student.new(name: "suzuki", age: 20)
expect(student.age).to eq 10
end
end
end
describe "introduce" do
it "正しい名前と年齢を含む値が返ること" do
student = Student.new(name: "suzuki", age: 10)
expect(student.introduce).to eq "私はsuzukiです。年齢は20才です。"
end
end
end
下記のような実行結果となる。
Student
initialize
インスタンスが生成された時
名前の値が正しいこと
年齢の値が正しいこと (FAILED - 1)
introduce
正しい名前と年齢を含む値が返ること (FAILED - 2)
Failures:
1) Student initialize インスタンスが生成された時 年齢の値が正しいこと
Failure/Error: expect(student.age).to eq 10
expected: 10
got: 20
(compared using ==)
# ./spec/student_spec.rb:11:in `block (4 levels) in <top (required)>'
2) Student introduce 正しい名前と年齢を含む値が返ること
Failure/Error: expect(student.introduce).to eq "私はsuzukiです。年齢は20才です。"
expected: "私はsuzukiです。年齢は20才です。"
got: "私はsuzukiです。年齢は10才です。"
(compared using ==)
# ./spec/student_spec.rb:19:in `block (3 levels) in <top (required)>'
Finished in 0.01333 seconds (files took 0.48402 seconds to load)
3 examples, 2 failures
Failed examples:
rspec ./spec/student_spec.rb:9 # Student initialize インスタンスが生成された時 年齢の値が正しいこと
rspec ./spec/student_spec.rb:17 # Student introduce 正しい名前と年齢を含む値が返ること
実行結果を見てみると、3 examples, 2 failures
「3つのテストを実行して、2つ失敗した」とある。
expected: 10
には、「期待していた値」が表示されている。
got: 20
には、「実際の値」が表示されている。
つまり、ここの差異が失敗の原因を示している。
上記では1) Student initialize
と2) Student introduce
の2つが表示されているので、2つの失敗が表示されていることになる。
また問題のある行数も具体的に記述されているので探しやすい。
#設定ファイルをカスタマイズ(.rspec)
# rails_helperを読み込む
--require rails_helper
# 出力結果をドキュメント風に見やすくする
--format documentation
# 出力結果を色分けする
--color
必要に応じて.rspec
に設定項目を追加。不要なものは削除する。
#テストコードの見方
#テスト対象を読み込むための記述
require_relative '../lib/ファイル名'
# 基本的な見方
RSpec.describe クラス名 do
describe "対象(例:バリデーション)" do
context "条件(例:データがA条件を満たす時)" do
before do
end
it "期待する結果(例:保存できる)" do
example(式).to eq 期待値
end
end
end
end
コード | 説明 |
---|---|
RSpec.describe *** do ... end | テストの宣言。これがないと正しく動作しない。 |
describe "***" do ... end | テストの対象。 |
context "***" do ... end | 条件別にグループ化(describeと機能的には同じ) |
before do ... end | 事前に処理を行うグループ。 |
it "***" do ... end | 期待する処理と結果をコメント |
example(X).to eq Y | テストコード。 |
コメントの部分は . # '' "" など書き方が色々あるようだが挙動は変わらないため統一されていれば、どれでもいいみたい。 |
#マッチャ
引用
マッチャ(matcher)は「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」のことです。
引用元:https://qiita.com/jnchito/items/2e79a1abe7cd8214caa5
コードで言うと、exampleの行にある、ep
の部分のところみたい。
# 「Xの結果とYは等しい」を検証
expect(X).to eq Y
# 「Xの結果とYは異なる」を検証
expect(X).not_to eq Y
# be_falsey/be_truthyもマッチャ(言語仕様に合わせた真偽を判定)
expect(X).to be_truthy
expect(X).to be_falsey
# changeもマッチャ(XするとYが[Z~W]or[±Z]に変わることを期待する)
expect{X}.to change{Y}.form(Z).to(W)
expect{X}.to change{Y}.by(±Z)
# includeもマッチャ(「配列XにYが含まれている」を検証)
X = [Y, Z, W]
expect(X).to include Y
参考元の詳細が大変参考になった。
書籍も買ってみたので具体的な部分についてはまた記事にまとめたい。
参考元
RSpec/公式ドキュメント
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」
RailsじゃないRspec3環境を構築する方法