如何在Rspec中存根Class对象?

时间:2015-02-16 10:01:53

标签: ruby oop testing rspec

我正在努力强制执行封装(但可能做得不好),并希望在Rspec中测试代码。 Customer类在工厂类中实例化时将采用类Object(作为klass)。通过尚未存在的UI,客户将创建订单。

我目前的测试如下。我只是想确认订单是Order类。

describe 'Customer' do 
  let(:customer){Customer.new}
  let(:customer_with_instantiation){Customer.new(:klass=>order, :name=>'Oscar Wilde', :number=>'0234567')}  

  let(:order){double :order, :name=>:order}

  it 'klass object to be the order class when customer is instantiated with a klass attribute' do
    expect(customer_with_instantiation.klass).to be_a(order)
  end  

end  

类代码如下:

    class Customer

      attr_accessor :name, :number, :klass

      DEFAULT_CUSTOMER_ORDER = {:order_detail => [{ :dish=>"", 
                                                    :item_count=>0 }], 
                                :order_total_cost=>0 }

      def initialize(options={})
        @name=options.fetch(:name, "")
        @number=options.fetch(:number, "")
        @klass=options.fetch(:klass, Object)
        @customer_order=DEFAULT_CUSTOMER_ORDER
      end  

      def place_order(menu)
        #requires user input
        customer_order=klass.new({:order_detail => [{:dish => :pizza, :item_count => 3}, 
                                                    {:dish => :burger, :item_count => 3}],
                                  :order_total_cost => 210})
        klass.test_customer_order(customer_order, self)
      end  

    end



  class Order

     attr_reader :order_detail, :order_total_cost
     attr_accessor :total_check 



 def initialize(options={})
    @order_detail=options.fetch(:order_detail, Object)
    @order_total_cost=options.fetch(:order_total_cost, Object)
  end  

  def self.test_customer_order(customer_order, customer, menu, assistant)
    customer_order.total_check = 0
    customer_order.order_detail.each do |order_item|
      menu.dishes.each do |dish|
        if order_item[:dish]==dish.name
          customer_order.total_check += dish.price*order_item[:item_count]
        end  
      end  
    end
    assistant.take_order(customer_order, customer, customer_order.total_check)
  end 

end 

感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

使用be_a,您正在测试klassklass的实例,这可能不是您想要的。

在我看来,在测试initialize方法和klass的getter(这是你正在做的,实际上)时,你应该只对确认发送的内容感兴趣之后可以阅读Customer.new

所以也许是这样的:

class Foo
  attr_reader :klass
  def initialize(args)
    @klass = args.fetch(:klass)
  end
end

describe Foo do
  describe "#initialize" do
    let(:klass) { double }
    let(:instance) { Foo.new(klass: klass)}
    it "sets klass" do
      expect(instance.klass).to eq(klass)
    end
  end
end

一些一般观点:

  • 如果您想测试订单是否是klass的实例,您应该重写代码以便更容易测试
  • 在这种情况下,
  • klass不是一个非常有用的名称。目前尚不清楚为什么Customer需要一个klass。
  • 您希望将订单与客户分离,但客户显然会对订单的界面做出一些假设。你真的做到了什么吗?
  • 我建议不要将测试方法放在类中,而应放在测试文件中。
  • Object中使用fetch作为默认设置可能不是您想要的。首先,您可能希望它们是某个类的实例,而不是类对象。
  • 创建订单真的是Customer类实例的工作吗?如果要确保可以根据用户输入实例化任何类型的抽象顺序,那么单独的OrderCreator类可能更合适吗?此类可以接受用户数据和订单类以及受影响的客户。