はじめに
20200401: 修正
- FactoryGirl -> FactoryBotに修正しています
Rspecでテストしたい
コツコツとRedmineのプラグインを作っていますが、実はRailsをちゃんと勉強していなかったので、あとから「うわー、こんな書き方しちゃった恥ずかしい...」と思う日々です。
さて、自作プラグインにはなるべくテストを付けていこう、それもRspecでやってみようと思うようになりました。
FactoryBotも使いたい
ですが、残念ながらRedmine本体はRspecもFactoryBotも利用していません。
そこで、自作プラグインにspec/ディレクトリを作り、おまじないをしながらRspecでテストを書くようにしています。FactoryBot用の定義も、やはり自作プラグインのspec/factories 以下に置いています。
さて、このFactoryBotですが、慣れると欲が出て来ます。
そもそもちゃんと利用できるのか確認したい、なるべく楽をして関連付いたモデルのデータも一緒に作れるように定義したい...と思いますよね。
ただ、配置場所が自作プラグインの下の階層なので、普通にrails consoleしただけでは読み込んでもらえません....。
本題: ConsoleからFactoryBotを呼んでみる
ということで、タイトルの通り本題に入ります。
例として利用しているプラグインは、こちらです。
何もしない場合
まず、普通に何も指定せずに rails consoleを起動します。
そこから、プラグインの下の spec/factories/user.rb を呼び出してみると...。
$ bundle exec rails c --sandbox
Loading test environment in sandbox (Rails 4.2.7.1)
Any modifications you make will be rolled back on exit
irb(main):001:0> FactoryBot.build(:user)
ArgumentError: Factory not registered: user
from /xxxxxxx/vendor/bundle/ruby/2.3.0/gems/factory_girl-4.8.0/lib/factory_girl/registry.rb:24:in `find’
案の定、エラーになります><
definition_file_paths を利用する
なんのことはないのですが、プラグイン用のディレクトリのspec_helperでFactoryBotの読み込みパスを追加設定しているので、そちらをconsoleでも呼んであげるようにしてみます。
$ bundle exec rails c --sandbox
Loading test environment in sandbox (Rails 4.2.7.1)
Any modifications you make will be rolled back on exit
irb(main):002:0> FactoryBot.definition_file_paths = %w(plugins/redmine_issue_badge/spec/factories)
=> ["plugins/redmine_issue_badge/spec/factories"]
irb(main):003:0> FactoryBot.find_definitions
=> [“/xxxxxx/redmine-3.3/plugins/redmine_issue_badge/spec/factories”]
definition_file_pathsでパスを指定しただけではファイルを見てくれないので、find_definitionsも合わせて実施します。
パスを追加した結果
それでは、同じように FactoryBot.build(:user)を実行してみます。
irb(main):004:0> FactoryBot.build(:user)
=> #<User id: nil, login: "user1",
hashed_password: “xxxxxxxxxxxxxxxxxxxx",
firstname: "User1", lastname: "Test1", admin: false,
status: 1, last_login_on: nil,
language: "ja", auth_source_id: nil,
created_on: nil, updated_on: nil,
type: "User", identity_url: nil, mail_notification: "",
salt: "7599f9963ec07b5a3b55b354407120c0",
must_change_passwd: false, passwd_changed_on: nil>
こんな感じで上手くできました。
FactoryBotを便利にする
うまく読み込めそうなので、その他のモデル用のFactoryBotのファイルも作ってみます。
プラグインのテストではチケットトラッキングが利用できるのを前提にしているので、このあたりをまとめて設定できると良いですよね。
個別に作ってあとから関連付けでもいいのですが、トラッカーを作る際に、デフォルトステータスとプロジェクトも一緒に作れるようにしてみます。
# トラッカーを作成
FactoryBot.define do
factory :tracker do
sequence(:name) { |n| "tracker-name: #{n}" }
sequence(:position) { |n| n }
default_status_id 1
# トラッカーの定義と一緒にデフォルトステータスも作成
trait :with_default_status do
after(:build) do |tracker|
status = FactoryBot.create(:issue_status)
tracker.default_status_id = status.id
end
end
# trait :with_project do
trait :with_default_status do
after(:build) do |tracker|
project = FactoryBot.create(:project)
project.trackers << tracker
end
end
end
end
# トラッカー用のステータスを作成
FactoryBot.define do
factory :issue_status do
sequence(:name) { |n| "status-name: #{n}" }
sequence(:position) { |n| n }
is_closed false
end
end
FactoryBot.define do
factory :project do
sequence(:name) { |n| "project-name: #{n}" }
sequence(:description) { |n| "project-description: #{n}" }
sequence(:identifier) { |n| "project-#{n}" }
homepage 'http://ecookbook.somenet.foo/'
is_public true
end
end
実行してみる
では、traitも使って実行してみます。
irb(main):036:0> FactoryBot.create(:tracker, :with_default_status, :with_project)
(0.1ms) SAVEPOINT active_record_1
IssueStatus Exists (0.3ms) SELECT 1 AS one FROM "issue_statuses" WHERE "issue_statuses"."name" = 'status-name: 7' LIMIT 1
SQL (0.3ms) INSERT INTO "issue_statuses" ("name", "position") VALUES (?, ?) [["name", "status-name: 7"], ["position", 7]]
SQL (0.2ms) UPDATE "issue_statuses" SET position = position + 1 WHERE (position >= 7 AND id <> 14)
--- [ 中略 ] ---
(0.0ms) RELEASE SAVEPOINT active_record_1
=> #<Tracker id: 13, name: "tracker-name: 7",
is_in_chlog: false, position: 7,
is_in_roadmap: true, fields_bits: 0, default_status_id: 14>
なんとかできたようです。
まとめ
以上、ちょっとしたメモでした。
RedmineやRedmineのプラグインに限らず、諸事情でデフォルトの spec/ 以下以外にテストコードやfactoriesを配置したことがあったので、今回Qiitaのメモ的な記事としてアップさせていただきました。
Consoleを使うと、テストの前に作った関数が正しい値を返してくれるかどうかのチェックがしやすくなります。
こんな感じでFactoryBotも確認できるので、できるだけfixturesから卒業を進めていきたいと思います。