[英]Rails RSpec Controller Test
I'm trying to test that a Subscription
was actually created as part of my Company#create
controller method: 我正在尝试测试实际上是作为Company#create
控制器方法的一部分创建的Subscription
:
require 'rails_helper'
RSpec.describe CompaniesController, :type => :controller do
before do
user = build(:user, :company => nil)
login_with(user)
end
it "should have a subscription" do
company = build(:company).attributes
terms_accepted = { terms_accepted: true }
@company_params = company.merge(terms_accepted)
post :create, { company: @company_params }
# I need to expect something here like company.subscription.exists?
end
end
I've already created a test to verify a company
is created which is contained in the 4 lines I currently have in the test above. 我已经创建了一个测试来验证创建的company
,该company
包含在我上面的测试中当前包含的4行中。
I have a company
factory that also creates a subscription
: 我有一家company
工厂,该工厂还创建了一个subscription
:
FactoryGirl.define do
factory :company do
name "ACME Test"
address_1 "123 Shady Lane."
address_2 "Suite 400"
city "Testville"
state "Somewhere"
zip_code "12345"
has_payment_plan false
stripe_id "cus_34d434343e4e3e3"
locked false
association :subscription
end
end
And... I have a subscription
factory: 还有...我有一个subscription
工厂:
FactoryGirl.define do
factory :subscription do
company_id 1
trial true
custom nil
convert_date nil
per_device_price 3
trial_expires 5.days.from_now
freemium false
end
end
And here is my Company#create
controller method... (yes, it has alot of bloat, which I'm eventually moving to the model once I have a base-line of tests that work) : 这是我的Company#create
控制器方法... (是的,它有很多膨胀,一旦我有了一个可以正常工作的测试基准,我最终就会转向模型) :
def create
company = Company.new(company_params)
user = current_user
if company.save && params[:terms] == "1"
user.company = company
accepted = true
user.terms_accepted = accepted
user.save
Subscription.create(company_id: user.company.id, trial: true, charge_date: Date.today + 1.month, per_device_price: 5.00, trial_expires: Date.today + 5.days)
device = Device.create(is_registered: true, registered_date: Date.today, company_id: user.company.id, name: user.company.name, is_sent: true, sent_date: Date.today)
location = Location.create(company_id: user.company.id, identifier: device.identifier, name: company.name)
device.serial_number = device.identifier
NewDevice.new_device(user, device).deliver
flash[:success] = "Welcome!"
redirect_to root_path
else
flash[:notice] = "You did not accept the terms of service."
sign_out(user)
redirect_to root_path
end
end
I thought I was on the right track by just checking to see if Subscription was increased by 1
, as I did with my Company#create
test, but that's returning: 我以为我只是通过检查Subscription was increased by 1
来走上正轨,就像我对Company#create
测试所做的那样,但是返回的是:
Failures:
1) CompaniesController should have a subscription
Failure/Error:
expect {
post :create, { company: @company_params }
}.to change(Subscription, :count).by(1)
expected #count to have changed by 1, but was changed by 0
Am I doing this incorrectly? 我做错了吗?
In your spec file companies_controller_spec.rb
I would do something like this: 在您的规格文件companies_controller_spec.rb
我将执行以下操作:
let(:valid_params) {
attributes_for(:company).merge(terms_accepted: true)
}
context 'POST #create' do
it 'creates a company' do
expect do
post :create, params: { company: valid_params }, session: {}
end.to change(Company, :count).by(1)
end
it 'creates a subscription' do
expect do
post :create, params: { company: valid_params }, session: {}
end.to change(Subscription, :count).by(1)
end
end
If the second spec fails, you'll need to troubleshoot why. 如果第二个规范失败,则需要解决原因。 You are not doing any error handling for the creation of Subscription, Device, or Location. 您没有为创建订阅,设备或位置做任何错误处理。
In addition there are several other problems with your create
controller method that are beyond the scope of your question. 另外,您的create
controller方法还存在其他一些问题,这些问题超出了您的讨论范围。 However, I assume you will find and address those as you work through the code. 但是,我假设您在遍历代码时会找到并解决这些问题。
Lets start with the low hanging fruit - instead of an "unwrapped" hacky TOS you can simply make it a virtual attribute of Company. 让我们从低落的果实开始-可以简单地使其成为Company的虚拟属性,而不是“未包装的” hacking TOS。
class Company < ApplicationRecord
validates_acceptance_of :terms # makes a virtual attribute if no column exists
end
A major pain point in this controller is that you are doing a bunch of operations after you save the controller that should really be done before. 该控制器的一个主要痛点是,在保存了之前应该真正完成的控制器之后,您需要执行一系列操作。
In Rails when you save a parent record the child records will also be saved. 在Rails中,当您保存父记录时,子记录也将被保存。 You can use this to simplify the logic of this controller: 您可以使用它来简化此控制器的逻辑:
def create
company = Company.new(company_params)
subscription = company.subscriptions.new(
trial: true,
charge_date: 1.month.from_now,
per_device_price: 5.00,
trial_expires: 5.days.from_now
)
# ...
if company.save
# ...
else
# ...
end
end
You can use this together with validates_associated
so that you would get direct feedback. 您可以将其与validates_associated
一起使用,以便获得直接反馈。 You should be able to test it then by: 您应该可以通过以下方式对其进行测试:
require 'rails_helper'
RSpec.describe CompaniesController, :type => :controller do
before do
user = build(:user, :company => nil)
login_with(user)
end
describe "#create" do
let(:valid_attributes) { attributes_for(:company, terms: true) }
let(:invalid_attributes) { attributes_for(:company, terms: false) }
describe "with valid attributes" do
it "creates a company" do
expect {
post :create, { company: valid_attributes }
}.to change(Company, :count).by(+1)
end
it "creates a subscription" do
expect {
post :create, { company: valid_attributes }
}.to change(Subscription, :count).by(+1)
expect(Subscription.last.company).to eq Company.last
end
end
describe "with invalid attributes" do
it "does not create a company" do
expect {
post :create, { company: attributes }
}.to_not change(Company, :count)
end
it "does not creates a subscription" do
expect {
post :create, { company: invalid_attributes }
}.to_not change(Subscription, :count)
end
end
end
end
However I can't help but wonder if part of this logic really should be attached to the user and not the company - if you are keeping track of login devices and locations it should be done in your SessionsController
or wherever the user signs in. 但是,我不禁要问,是否应该将此逻辑的一部分确实附加给用户而非公司?-如果您要跟踪登录设备和位置,则应在SessionsController
或用户登录的任何地方进行。
The problem is you don't pass terms: '1'
as a request param in your test. 问题是您没有通过terms: '1'
作为测试中的请求参数。 Instead of that you pass terms: true
(which isn't the same as '1') and also you put it inside the company
attributes
hash. 取而代之的是传递terms: true
(与“ 1”不同),然后将其放在company
attributes
哈希中。 So you need either replace the condition in your action to if company.save && params[:terms] == true
or pass '1'
in your test. 因此,您需要将操作中的条件替换为if company.save && params[:terms] == true
或在测试中通过'1'
。
That should work 那应该工作
it "should have a subscription" do
company = build(:company).attributes
expect {
post :create, { company: company, terms: '1' }
}.to change(Subscription, :count).by(1)
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.