7327


「一定期間以上 既読状態なら未読に戻す」というクラスメソッドへのRSpecを書きたいのですが動作しません。

テストはこのように書き

[code]

      let(:company){ Company.create }
      describe ".remove_old_read" do
        before{ Timecop.freeze(Time.local(2014,10,10)) }
        after{ Timecop.return }
        context "既読で3ヶ月以上前なら" do
          before do
            company.update_attributes(read: true, read_at: Time.new(2014,7,9))
            Company.remove_old_read
          end
          it "未読にする" do
            expect(company.read).to eq(false)
          end
        end
      end
    
[/code]

対するメソッドはこのように書いています。

[code]

    class Company < ActiveRecord::Base
      def self.remove_old_read
        Company.where("read_at < ?", Time.zone.now - 3.month).each do |c|
          c.update_attributes({read: false}) #if c.read 
        end
      end
    end
    
[/code]

どこが間違えているのでしょうか？  
特にクラスメソッドの呼び出し方法が自信ありません…。


CC BY-SA 3.0
2015-03-02T10:15:33.713
7323
2015-03-02T11:40:03.870
3271
question
ruby ruby-on-rails
一括処理をするクラスメソッドに対するRspecの書き方


僕が書くとしたらこんな感じですかねー。

[code]

    describe ".remove_old_read" do
      context "既読で3ヶ月以上前なら" do
        before do
          company.update_attributes(read: true, read_at: Time.local(2014, 7, 9))
        end
        after{ Timecop.return }
        it "未読にする" do
          Timecop.freeze(Time.local(2014, 10, 10)) 
          expect { Company.remove_old_read }.to change{ company.reload.read }.from(true).to(false)
        end
      end
    end
    
[/code]

`Company.remove_old_read`はテストコード上は`company`と全く関連のないメソッドになっています。  
なので`company`は`reload`してあげないとDBの状態とテストコード上の状態がシンクしません。  
たぶんここがポイントじゃないかなと思います。

それから、`context
"既読で3ヶ月以上前なら"`って書いているなら、Timecopを使ったシステム日時の変更はこのcontextブロックの中で書いてあげましょう。  
また、`remove_old_read`の近くで変更したほうが「これは2014-10-10にremove_old_readするんだな」というのが見た目にわかりやすくていいと思います。

あと`change`マッチャについてはこちらを参照してください。

  * <http://qiita.com/jnchito/items/2e79a1abe7cd8214caa5#change--from--to--by>

参考になれば幸いです。

### 別解

read_atを3ヶ月前の日時で更新すればTimecopはいらないですね。

[code]

    describe ".remove_old_read" do
      context "既読で3ヶ月以上前なら" do
        before do
          # 3ヶ月前ピッタリだとタイミングによっては条件に合致しないかもしれないので、
          # さらに1分追加しておく。
          company.update_attributes(read: true, read_at: 3.months.ago - 1.minute)
        end
        it "未読にする" do
          expect { Company.remove_old_read }.to change{ company.reload.read }.from(true).to(false)
        end
      end
    end
    
[/code]


CC BY-SA 3.0
2015-03-02T11:26:44.227
7327
2015-03-02T11:40:03.870
2015-03-02T11:40:03.870
85
85
7323
answer