本記事では、検索機能のモデル単体テストの実施手順を、詳しく解説します。LIKE句を利用した「あいまい検索」の実装方法はこちらをご覧ください。
成功画面
まず、以下が本テスト成功の結果コードです。
テストは様々な検証方法がありますので、こちらはあくまで一例です。本記事ではこの結果を目指し、準備を進めていきます。
# テスト実行(ファイル指定)
bundle exec rspec spec/models/item_spec.rb
# テスト結果
Item
#search
一致するデータが存在する場合
検索文字列に完全一致する配列を返すこと
検索文字列に部分一致する配列を返すこと
updated_at降順で配列を返すこと
price降順で配列を返すこと
価格が15,000円〜25,000円に含まれるのitemが1件抽出されること
一致するデータが存在しない場合
検索文字列が一致しない場合、空の配列を返すこと
検索文字列が空白の場合、すべての配列を返すこと
Finished in 1.79 seconds (files took 5.44 seconds to load)
7 examples, 0 failures
単体テスト実施の流れ
検索機能の単体テストは、大まかに以下の流れで実施します。
①事前準備
②テスト内容の設計
③テストファイルの作成
④テスト実施/検証
実施手順
⓪はじめに
・テストで使用する言語について
Ruby on Railsでは、RSpecという独自の言語を利用してテストを行います。
そしてRspecは、「rspec-rails」というgemのインストールすることで使用することができます。
・ダミーデータの作成について
RSpecを使用したテストを行う場合、FactoryBotを使って、簡単にダミーデータ(モデルインスタンス)を生成することができます。こちらもRSpec同様、「factory_bot_rails」というgemのインストールし、使用します。
①事前準備
ここでは「rspec-rails」「factory_bot_rails」の準備を、以下手順で行います。
a) RSpecの導入
b) RSpecの設定
c) RSpecの利用に向けた事前検証
d) factory_botの導入
e) factory_botでダミーデータ(インスタンス)生成
それでは実際にこれらの準備に進みましょう。
a) RSpecの導入
まず以下のように、「rspec-rails」「web-console」というgemを追記します。
※「web_console」はtest環境で動かすと不具合が起きる可能性があるため、develop環境のみに記述します。
group :development, :test do
gem 'rspec-rails'
end
group :development do
gem 'web-console'
end
編集後、bundle installをします。
$ bundle install
b) RSpecの設定
次にRSpecの基本設定として、RSpec用の設定ファイルを作成します。
以下を実行すると、create以下のファイルが作成されます。
$ rails g rspec:install
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
RSpecの設定は以上です。
次に、RSpecが正常に機能するかの確認を行います。
c) RSpecの利用に向けた事前検証
事前検証は以下のコマンドを実行して、「No example ..」以下の結果となれば、準備完了です。
$ bundle exec rspec
No examples found.
Finished in 0.00106 seconds (files took 1.12 seconds to load)
0 examples, 0 failures
d) factory_botの導入
RSpecの事前検証を終えたら、ダミーデータを生成するfactory_botを導入します。
これは先ほどのgemfile内、RSpecと同じグループに追記、bundle installをして完了です。
group :development, :test do
gem 'rspec-rails'
gem 'factory_bot_rails'
end
e) factory_botでダミーデータ(インスタンス)生成
続いて、以下のように「factories」ディレクトリを作成し、その中に「items.rb」(作成したインスタンスの複数形のファイル名)という名前のファイルを作成します。
app
spec
models
specファイル
controllers
specファイル
factories
items.rb ⇦ 作成ファイル
このitems.rbに、ダミーデータを作成します。
この準備によって、specファイル(テストコード記述ファイル)から指定のメソッドにより、ダミーデータを元にインスタンスの生成や、DB保存が可能になります。
※カラムはご自身のDBに合わせてください。
※値は適当で構いません。
事前準備は以上です。少し長いですが、やっていることはシンプルですね。
それではテスト内容の設計に進みましょう。
②テスト内容の設計
テスト内容には、基本的に、期待する結果と期待しない結果の両方を盛り込みます。
その際、テストしたい機能の内容を踏まえて、パターンを洗い出していくと整理しやすいかと思います。
今回はざっくりですが、以下の図のように洗い出しました。
並び替え検索の「XXの昇順」は、例えば「商品を価格が昇順で並び替え検索」。範囲検索の「XXがA〜Bに含魔れる」は、「価格が1,000〜5,000円の商品を範囲検索」、などが挙げられます。
それでは以上で洗い出したテスト内容を、対象のspecファイルに記述していきます。
③テストファイルの作成
今回はモデルの単体テストなので、テストファイルを spec/models/ に「item_spec.rb」の名前で作成します。
app
spec
models
item_spec.rb ⇦ 作成ファイル
controllers
specファイル
factories
items.rb
そして先ほど洗い出した条件をもとに、以下のようにテスト処理を記述します。
require 'rails_helper'
describe '#search' do
context "一致するデータが存在する場合" do
# filter
it "検索文字列に完全一致する配列を返すこと" do
item = create(:item)
# "test-name1"と一致するデータがitemに存在することを期待する
expect(Item.search("test-name1")).to include(item)
end
it "検索文字列に部分一致する配列を返すこと" do
item = create(:item)
# "t"を含むデータがitemに存在することを期待する
expect(Item.search("t")).to include(item)
end
# sort
it "updated_at降順で配列を返すこと" do
create(:item)
create(:item2)
create(:item3)
# 更新日時の古いid順は「2,3,1」であることを期待する
expect(Item.order("updated_at DESC").map(&:id)).to eq [2,3,1]
end
it "price降順で配列を返すこと" do
create(:item)
create(:item2)
create(:item3)
# 価格の高いid順は「3,1,2」であることを期待する
expect(Item.order("price DESC").map(&:id)).to eq [3,1,2]
end
# between
it "価格が15,000円〜25,000円に含まれるのitemが1件抽出されること" do
create(:item)
create(:item2)
create(:item3)
items = Item.where(price: 15000..25000)
# 価格が15000~25000に含まれるデータ「1件」であることを期待する
expect(items.count).to eq 1
end
end
context "一致するデータが存在しない場合" do
it "検索文字列が一致しない場合、空の配列を返すこと" do
item = create(:item)
# "あああ"と一致するデータがitemに存在しないことを期待する
expect(Item.search("あああ")).to be_empty
end
it "検索文字列が空白の場合、すべての配列を返すこと" do
item = create(:item)
# 検索文字列が空の場合、すべてのデータが返されることを期待する
expect(Item.search("")).to include(item)
end
end
end
end
④テスト実施/検証
テストコードの記述を終えたら、テストを実行します。そしてItem以下の結果となれば、無事テスト成功です。
$ bundle exec rspec spec/models/item_spec.rb
Item
#search
一致するデータが存在する場合
検索文字列に完全一致する配列を返すこと
検索文字列に部分一致する配列を返すこと
updated_at降順で配列を返すこと
price降順で配列を返すこと
価格が15,000円〜25,000円に含まれるのitemが1件抽出されること
一致するデータが存在しない場合
検索文字列が一致しない場合、空の配列を返すこと
検索文字列が空白の場合、すべての配列を返すこと
Finished in 1.79 seconds (files took 5.44 seconds to load)
7 examples, 0 failures
おわりに
検索機能のモデル単体テストの実施手順は以上になります。
中でも設計部分について、どんなテストを実行すれば良いか分からなくなった場合は、まず「期待する結果と期待しない結果の両方」を紙などに洗い出すと良いかと思います。
また検索機能の実装自体は、LIKE句を使用した方法は以下をご参照ください。
高度な検索機能を実装をできる「ransack」というgemを使用した実装方法は以下にまとめていますので、よろしければご参照ください。