indilog

Ruby/Rails/React/Goなどに関して自分が勉強したことなどを投稿しています

STIを利用している場合のFactoryBotのfactory定義について

STIを活用しているモデルは、スーパークラスでfactoryを定義して、テストでオブジェクトの比較をすると、スーパークラスのままのものと、 サブクラスに変換されているものとの比較になってしまうことがあり、そのためテストが落ちることがある

以下のように書くことによって、typeを元にSTIのクラスを取得し、それを使ってオブジェクトが初期化できるようになる

FactoryBot.define do
  factory :factory_name do
    initialize_with do
      klass = type.constantize
      klass.new(attributes)
    end

    # 以下factoryの定義
  end
end

RSpecのexpectとis_expectedの挙動の違いについて

subject(:actual_object) { ... }

it { is_expected.to eq(expected_object) }

subject(:actual_object) { ... }

it { expect(actual_object).to eq(expected_object) }

では挙動が異なるという話

上の方を actual_objectexpected_object を同じ属性などを定義した状態で比較しようとすると、

Compared using equal?, which compares object identity, but expected and actual are not the same object. Use expect(actual).to eq(expected) if you don't care about object identity in this example

という注意書きが表示され、上の方法は同じオブジェクトかどうかをobjectのIDで判定していることがわかる

一方で下の方は属性の一致で比較をするため、別オブジェクトであっても、属性さえ一致していればテストが通ることになる

つまり、同じオブジェクトかどうかを比較したい場合は上の方法、同じ属性を持っているかどうかを比較したい場合は下の方の方法を使えば良さそうだ

Model周りのRSpecを書く時に意識していること

正しい方法かはわからないけど、最近Model周りのテストコードを書いていて自分でしっくりくるような書き方を考え出したのでつらつらと書いていきたい

テストコードの基本形

あるModel内のインスタンスメソッドをテストする場合の基本形としては、このような形になってくる

describe '#instance_method' do
  subject { instance.instance_method }

  context 'when first context' do
    let(:instance) { build(:factory_for_first_context) }

    it { is_expected.to ... }
  end

  context 'when second context' do
    let(:instance) { build(:factory_for_second_context) }

    it { is_expected.to ... }
  end
end

意識している箇所

1. describeの直下の行にはsubjectを置く

describe '#instance_method' do
  subject { instance.instance_method }

このようにdescribeでテストしたいメソッドを記述し、その下に今回のテスト対象をsubjectと利用して定義する

2. contextでそのコンテキストに合致したinstanceを定義

  context 'when first context' do
    let(:instance) { build(:factory_for_first_context) }

contextで条件を記述しつつ、それに対応したinstanceをなかで定義することにより、その下の

    it { is_expected.to ... }

でsubjectが評価される時に、contextのスコープで定義したinstanceが採用されるようになる 別のコンテキストでテストしたい時も同じようにそのcontextのスコープで定義を行えば、同等の結果が得られるので、すっきりとした印象のテストができる

Shrineでdelete_rawプラグイン使用時に元の画像を削除せずにseedデータを作成する

seed_fuを利用し、

Model.seed do |s|
  s.image = File.new(image_path)
end

としてseedデータ登録時にShrineを用いて画像を登録するようにしていたら、 seedデータ作成時にdelete_rawのプラグインを入れていたため、元のファイルが削除される形になってしまっていた

そこでここを参考に Shrine(2.7.0) is destroying my source file, when using it with rspec · Issue #193 · shrinerb/shrine · GitHub

Model.seed do |s|
  s.image = StringIO.new(File.binread(image_path)
end

と奇術師、StringIOのオブジェクトを渡してあげることによって、元のデータを保ったままデータがアップロードできるようになった🎉

Heroku PostgreでStagingのデータをReview Appsにimportする方法

herokuのreview appsを使っていて、review appsでstagingにあるデータをそのまま使ってデータがある状態で検証を行いたいと思い、とりあえずインポートする方法を探してみたところ、以下の方法でできた💡

手順

  1. Stagingのherokuの管理画面からOverview⇨Heroku Postgres f:id:indigo-lain:20180628095239p:plain

  2. Durabilityタブ内のBackupからDownload f:id:indigo-lain:20180628095308p:plain

  3. DownloadしてきたファイルをDropbox等外からアクセスできる場所にアップロードする

  4. Review AppsのHeroku PostgresからImport先のDBの名称を確認 f:id:indigo-lain:20180628095333p:plain

  5. 以下コマンドを実行する ここ 参照

heroku pg:backups:restore <"DropboxにあげたファイルのURL"> <Import先のDB名> <appの指定など>

peco+historyで便利なhistory

設定

brew install peco でpecoをインストール

~/.zshrc で以下を追記

function peco-select-history() {
  BUFFER=$(\history -n -r 1 | peco --query "$LBUFFER")
  CURSOR=$#BUFFER
  zle clear-screen
}
zle -N peco-select-history
bindkey '^r' peco-select-history

使い方

Ctrl+r でpecoを使ってインクリメンタルに history から検索、実行ができて便利 ✨