【问题标题】:How do I test stripe and my controller effectively?如何有效地测试条带和我的控制器?
【发布时间】:2015-05-11 06:48:00
【问题描述】:

我有以下型号: subscriptionuserevents

  • 一个userhas_onesubscription
  • subscriptionbelongs_touser
  • userhas_manyevents
  • eventbelongs_touser

到目前为止,我已经能够使用 Capybara 和 RSpec 成功创建验收测试。这使我可以“升级”用户帐户(添加不同的角色)。我还能够进行验收测试,用户取消订阅并确保他们的角色被删除。

但是,现在我想确保取消任何用户的打开事件。这就是我卡住的地方。实际上,我什至没有走到这一步,因为我在尝试销毁订阅时遇到了麻烦。

因此,我创建了一个名为 subscriptions_controller_spec.rb 的控制器规范。在本规范中,有一个测试可确保 destroy 操作按预期工作。这是失败的,因为在我的控制器中,它会检索不存在的客户和订阅,并返回 Stripe::InvalidRequestError

为了解决这个问题,我尝试使用stripe-ruby-mock 来模拟条带服务器。但是,我不确定我应该如何在控制器规范中使用它,我真的很困惑。下面是我的控制器和我的控制器规格。任何关于我应该如何解决这个问题的建议都会非常感激。

subscriptions_controller_spec.rb

require 'rails_helper'

RSpec.describe SubscriptionsController, :type => :controller do

  let(:stripe_helper) { StripeMock.create_test_helper }
  before { StripeMock.start }
  after { StripeMock.stop }

  # ... omitted 

  describe 'DELETE destroy' do
    before :each do
      sign_in_trainer
      @subscription = create(:subscription, user: subject.current_user)
      plan = stripe_helper.create_plan(:id => 'Standard')
      customer = Stripe::Customer.create({
                                            email: 'johnny@appleseed.com',
                                            source: stripe_helper.generate_card_token,
                                            plan: 'Standard'
                                        })
      @subscription.customer_id = customer.id
      @subscription.stripe_sub_id = customer.subscriptions.data.first.id
    end

    it 'destroys the requested subscription' do
      expect {
        delete :destroy, {:id => @subscription.to_param}
      }.to change(Subscription, :count).by(-1)
    end

    # ... omitted

  end
end

还有subscriptions_controller.rb

class SubscriptionsController < ApplicationController
  before_action :set_subscription, only: [:update, :destroy]

  # ... ommitted

  # DELETE /cancel-subscriptions/1
  def destroy
    begin
      customer = Stripe::Customer.retrieve(@subscription.customer_id)
      customer.subscriptions.retrieve(@subscription.stripe_sub_id).delete
    rescue Stripe::CardError => e
      # User's card was declined for many magnitude of reasons
      redirect_to user_dashboard_path, alert: 'There was a problem cancelling your subscription' and return
    rescue Stripe::APIConnectionError => e
      # Stripe network issues
      redirect_to user_dashboard_path, alert: 'Network issue. Please try again later' and return
    rescue Stripe::APIError => e
      # Stripe network issues
      redirect_to user_dashboard_path, alert: 'Network issue. Please try again later' and return
    rescue Stripe::InvalidRequestError => e
      # This is something that we screwed up in our programming. This should literally never happen.
      redirect_to user_dashboard_path, alert: 'There was a problem cancelling your subscription.' and return
    rescue => e
      logger.error e.message
      logger.error e.backtrace.join("\n")
      redirect_to user_dashboard_path, alert: 'There was a problem cancelling your subscription.' and return
    end

    if current_user.events
      @events = current_user.events
      @events.open.each do |event|
        event.cancel
      end
    end

    current_user.remove_role 'trainer'
    current_user.add_role 'user'
    current_user.save
    @subscription.destroy
    respond_to do |format|
      format.html { redirect_to user_dashboard_path, notice: 'Subscription cancelled. All your open events have been cancelled.' }
      format.json { head :no_content }
    end
  end

  private
  # Use callbacks to share common setup or constraints between actions.
  def set_subscription
    @subscription = Subscription.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def subscription_params
    params[:subscription]
  end
end

【问题讨论】:

  • 你模拟条纹的部分在哪里?
  • 我认为我可能还需要做的是将我的工厂添加到问题中。该死的,通过 SO 有效地询问这是一个复杂的问题。
  • 这里是 GitHub:github.com/rebelidealist/stripe-ruby-mock - 老实说,我也不知道它是否应该这样使用。第一个主要的 Rails 站点,第一次使用 Stripe。我深陷其中!
  • 看起来很有趣。当然值得研究。看起来你做的事情与预期没有什么不同。他们在这里有一个聊天服务器:gitter.im/rebelidealist/stripe-ruby-mock 如果你想问他们。
  • 如果您对@TarynEast 感兴趣,我会按照 ridget 的示例将其移至服务对象中。一切正常,但现在我将重构测试以适应变化。

标签: ruby-on-rails rspec stripe-payments


【解决方案1】:

我认为您在这里已经一针见血了,很难在控制器规范中进行测试这一事实表明,现在可能是考虑将行为转移到服务类的好时机。

我要做的是设置一个集成测试以用作您的反馈循环,然后重构并恢复绿色。完成此操作后,开始重构您的服务类并从那里构建您的规范。

【讨论】:

  • 谢谢汤姆...不过,我真的不知道从哪里开始将其移至服务类。您能在网上推荐任何可能为我指明正确方向的材料吗?
  • patrick,对不起,伙计,服务类只是专门用于任务的类的一个花哨的术语。如果您想配对,请在我的工作电子邮件上联系我,与此同时,我认为 Taryns 的回答应该可以帮助您在短期内通过这些测试。
  • 不能说我实际上已经阅读/使用过这个,但谷歌指出了这一点——而且似乎甚至以 Stripe 为例......blog.engineyard.com/2014/…
  • ridget,你的工作邮箱是什么? @TarynEast - 看起来很有希望。谢谢
  • @TarynEast 的帖子绝对是一个很棒的例子,patrick 我已经在 github 上用我的联系方式联系了你。
【解决方案2】:

简单地模拟 Stripe 不起作用,例如:

require 'rails_helper'

RSpec.describe SubscriptionsController, :type => :controller do

  # ... omitted 

  describe 'DELETE destroy' do
    before :each do
      sign_in_trainer
      @subscription = create(:subscription, user: subject.current_user)
    end

    it 'destroys the requested subscription' do
      # just mock stripe to pass back the customer you expect - as though it Just Works
      expect(Stripe::Customer).to receive(:retreive).and_return(subscription.customer)

      expect {
        delete :destroy, {:id => @subscription.to_param}
      }.to change(Subscription, :count).by(-1)
    end


    it 'does not destroy it if we got a card error' do
      # likewise you can mock up what happens when an error is raised
      expect(Stripe::Customer).to receive(:retreive).and_raise(Stripe::CardError)

      expect {
        delete :destroy, {:id => @subscription.to_param}
      }.not_to change(Subscription, :count)
    end
    # ... omitted

  end
end

【讨论】:

  • 好的,谢谢。我打算今天下午试一试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
  • 2017-06-28
相关资源
最近更新 更多