我如何有效地测试条带和我的控制器?
How do I test stripe and my controller effectively?
我有以下型号:
subscription
、user
和 events
- 一个
user
has_one
subscription
- 一个
subscription
belongs_to
一个user
- 一个
user
has_many
events
- 一个
event
belongs_to
一个user
到目前为止,我已经能够使用 Capybara 和 RSpec 成功创建验收测试。这允许我 'upgrade' 一个用户帐户(添加不同的角色)。我还能够进行验收测试,用户取消他们的订阅并确保他们的角色被删除。
但是,现在我想确保取消用户的任何打开事件。这就是我被困的地方。实际上,我什至没有走到这一步,因为我 运行 在试图破坏订阅时遇到了麻烦。
因此,我创建了一个名为 subscriptions_controller_spec.rb
的控制器规范。在此规范中,有一项测试可确保 destroy
操作按预期工作。这是失败的,因为在我的控制器中它会检索不存在的客户和订阅,以及 returns 和 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
我认为您在这里已经说到了要点,很难在控制器规范中进行测试这一事实表明,现在可能是考虑将行为转移到服务的好时机 class。
我要做的是设置一个集成测试以用作您的反馈循环,然后重构并返回到绿色。完成后,开始重构您的服务 class 并从那里构建您的规范。
简单模拟 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
我有以下型号:
subscription
、user
和 events
- 一个
user
has_one
subscription
- 一个
subscription
belongs_to
一个user
- 一个
user
has_many
events
- 一个
event
belongs_to
一个user
到目前为止,我已经能够使用 Capybara 和 RSpec 成功创建验收测试。这允许我 'upgrade' 一个用户帐户(添加不同的角色)。我还能够进行验收测试,用户取消他们的订阅并确保他们的角色被删除。
但是,现在我想确保取消用户的任何打开事件。这就是我被困的地方。实际上,我什至没有走到这一步,因为我 运行 在试图破坏订阅时遇到了麻烦。
因此,我创建了一个名为 subscriptions_controller_spec.rb
的控制器规范。在此规范中,有一项测试可确保 destroy
操作按预期工作。这是失败的,因为在我的控制器中它会检索不存在的客户和订阅,以及 returns 和 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
我认为您在这里已经说到了要点,很难在控制器规范中进行测试这一事实表明,现在可能是考虑将行为转移到服务的好时机 class。
我要做的是设置一个集成测试以用作您的反馈循环,然后重构并返回到绿色。完成后,开始重构您的服务 class 并从那里构建您的规范。
简单模拟 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