简体   繁体   English

Rails:如何测试state_machine?

[英]Rails: How to test state_machine?

Please, help me. 请帮我。 I'm confused. 我糊涂了。 I know how to write state-driven behavior of model, but I don't know what should I write in specs... 我知道如何编写模型的状态驱动行为,但我不知道我应该在规范中写什么...

My model.rb file look 我的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

I use state_machine gem. 我使用state_machine gem。

Please, show me the course. 拜托,告诉我课程。

The question is old, but I had the same one. 问题很古老,但我有同样的问题。 Taking example from state_machine gem : 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

My solution was: 我的解决方案是:

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

I was using: 我用的是:

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

The state_machine_rspec gem includes many helper methods for writing concise specs. 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

These RSpec matchers will assist with the state_machine specs from a high-level. 这些RSpec匹配器将协助处理来自高级别的state_machine规范。 From here, one needs to write the specs for the business cases for can_approve? 从这里开始,需要为can_approve?编写业务案例的can_approve? , can_divert? can_divert? , and can_repeat? can_repeat? .

I have written a RSpec custom matcher. 我写了一个RSpec自定义匹配器。 It allows to test state flow in elegant and simple way: check it out 它允许以优雅和简单的方式测试状态流: 检查它

Unfortunately, I think you need to put a test for each state -> state transition, which might feel like code duplication. 不幸的是,我认为你需要对每个状态进行测试 - >状态转换,这可能感觉像是代码重复。

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

Fortunately, I think this usually fits into integration testing. 幸运的是,我认为这通常适用于集成测试。 For instance, an extremely simple state machine for a payments system would be: 例如,支付系统的一个非常简单的状态机将是:

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

You might still have the unit tests as above, but you'll probably also have integration testing, something like: 你可能仍然有如上所述的单元测试,但你可能也会进行集成测试,例如:

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

That was a lot of handwaiving, but hopefully that makes sense. 这是很多人的挥手,但希望这是有道理的。 I'm also not super used to RSpec, so hopefully I didn't make too many mistakes there. 我也不是很习惯RSpec,所以希望我没有犯太多错误。 If there's a more elegant way to test this, I haven't found it yet. 如果有一种更优雅的方式来测试它,我还没有找到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM