【问题标题】:Rails: How to test state_machine?Rails:如何测试 state_machine?
【发布时间】:2010-06-15 17:46:46
【问题描述】:

请帮帮我。我很困惑。我知道如何编写模型的状态驱动行为,但我不知道我应该在规范中写什么...

我的model.rb文件看起来

class Ratification < ActiveRecord::Base
  belongs_to :user

  attr_protected :status_events

  state_machine :status, :initial => :boss do
    state :boss
    state :owner
    state :declarant
    state :done

    event :approve do
      transition :boss => :owner, :owner => :done
    end

    event :divert do
      transition [:boss, :owner] => :declarant
    end

    event :repeat do
      transition :declarant => :boss
    end

  end
end

我使用state_machine gem。

请告诉我课程。

【问题讨论】:

  • 有什么方法可以在不访问数据库的情况下做到这一点?似乎我们应该能够剔除 state_machine 用来访问数据库的任何内容,并且仍然可以获得我们期望的所有更改。

标签: ruby-on-rails rspec state-machine


【解决方案1】:

这个问题很老,但我有同样的问题。以state_machine gem 为例:

class Vehicle
  state_machine :state, :initial => :parked do
    event :park do
      transition [:idling, :first_gear] => :parked
    end

    event :ignite do
      transition :stalled => same, :parked => :idling
    end

    event :idle do
      transition :first_gear => :idling
    end

    event :shift_up do
      transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
    end

    event :shift_down do
      transition :third_gear => :second_gear, :second_gear => :first_gear
    end
  end
end

我的解决方案是:

describe Vehicle do

  before :each do
    @vehicle = Factory(:vehicle)
  end

  describe 'states' do
    describe ':parked' do
      it 'should be an initial state' do
        # Check for @vehicle.parked? to be true
        @vehicle.should be_parked
      end

      it 'should change to :idling on :ignite' do
        @vehicle.ignite!
        @vehicle.should be_idling
      end

      ['shift_up!', 'shift_down!'].each do |action|
        it "should raise an error for #{action}" do
          lambda {@job_offer.send(action)}.should raise_error
        end
      end
    end
  end
end

我正在使用:

  • 红宝石 (1.9.3)
  • 导轨 (3.1.3)
  • rspec (2.8.0.rc1)
  • factory_girl (2.3.2)
  • state_machine (1.1.0)

【讨论】:

    【解决方案2】:

    state_machine_rspec gem 包含许多用于编写简明规范的辅助方法。

     describe Ratification do
       it { should have_states :boss, :declarant, :done, :owner }
       it { should handle_events :approve, when: :boss }
       it { should handle_events :approve, when: :owner }
       it { should handle_events :divert, when: :boss }
       it { should handle_events :divert, when: :owner }
       it { should handle_events :repeat, when: :declarant }
       it { should reject_events :approve, :divert, :repeat, when: :done }
       it { should reject_events :approve, :divert, :repeat, when: :done }
     end
    

    这些 RSpec 匹配器将协助处理高级别的 state_machine 规范。从这里开始,需要为can_approve?can_divert?can_repeat? 的业务案例编写规范。

    【讨论】:

      【解决方案3】:

      我写了一个 RSpec 自定义匹配器。它允许以优雅而简单的方式测试状态流:check it out

      【讨论】:

        【解决方案4】:

        不幸的是,我认为您需要对每个状态进行测试 -> 状态转换,这可能感觉像是代码重复。

        describe Ratification do
          it "should initialize to :boss" do
            r = Ratification.new
            r.boss?.should == true
          end
        
          it "should move from :boss to :owner to :done as it's approved" do
            r = Ratification.new
            r.boss?.should == true
            r.approve
            r.owner?.should == true
            r.approve
            r.done?.should == true
          end
        
          # ...
        end
        

        幸运的是,我认为这通常适合集成测试。例如,支付系统的一个极其简单的状态机是:

        class Bill < ActiveRecord::Base
          belongs_to :account
        
          attr_protected :status_events
        
          state_machine :status, :initial => :unpaid do
            state :unpaid
            state :paid
        
            event :mark_as_paid do
              transition :unpaid => :paid
            end
          end
        end
        

        您可能仍然需要进行上述单元测试,但您可能还会进行集成测试,例如:

        describe Account do
          it "should mark the most recent bill as paid" do
            @account.recent_bill.unpaid?.should == true
            @account.process_creditcard(@credit_card)
            @account.recent_bill.paid?.should == true
          end
        end
        

        这是一个很大的放弃,但希望这是有道理的。我也不太习惯 RSpec,所以希望我没有在那里犯太多错误。如果有更优雅的测试方法,我还没有找到。

        【讨论】:

        • 你可以使用... @account.recent_bill.should be_unpaid
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多