如何在 Rspec 中存根 Class 对象?

How do I stub a Class object in Rspec?

我正在努力执行封装(但可能做得不是很好),并想测试 Rspec 中的代码。客户 class 在工厂 class 中实例化时将采用 class 对象(作为 klass)。通过尚未存在的 UI,客户将创建一个订单。

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

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代码如下:

    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 

感谢任何帮助!

通过使用 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 需要一个类。
  • 您想将订单与客户解耦,但客户显然对订单的接口做了一些假设。你真的有所成就吗?
  • 我建议不要将测试方法放在 classes 中,而是放在测试文件中。
  • fetch 中使用 Object 作为默认值可能不是您想要的。首先,您可能希望它们是某些 class,而不是 class 对象的实例。
  • 创建订单真的是Customerclass实例的工作吗?如果重点是确保可以根据用户输入实例化任何类型的抽象订单,也许单独的 OrderCreator class 会更合适?此 class 可以接受用户数据和订单 class 以及受影响的客户。