Ruby on rails 优化Rails RSpec测试
我正在为我的Rails 4应用程序做一个测试,我对使用RSpec非常陌生。我有一个名为AppsController的控制器,它有标准索引、新建、显示、创建。。。方法和它们的工作方式都是Rails建议的,等等。“新建”创建一个对象的新实例,并实际创建保存它、显示、显示它和索引显示所有对象。以下是我目前的测试,有人能看到任何潜在的问题或我可以改进的地方吗Ruby on rails 优化Rails RSpec测试,ruby-on-rails,ruby,rspec,factory-bot,ruby-on-rails-4,Ruby On Rails,Ruby,Rspec,Factory Bot,Ruby On Rails 4,我正在为我的Rails 4应用程序做一个测试,我对使用RSpec非常陌生。我有一个名为AppsController的控制器,它有标准索引、新建、显示、创建。。。方法和它们的工作方式都是Rails建议的,等等。“新建”创建一个对象的新实例,并实际创建保存它、显示、显示它和索引显示所有对象。以下是我目前的测试,有人能看到任何潜在的问题或我可以改进的地方吗 FactoryGirl.define do factory :developer do email 'example@me.com'
FactoryGirl.define do
factory :developer do
email 'example@me.com'
password 'new_york'
password_confirmation 'new_york'
tos '1'
end
factory :app do
name 'New App'
tos '1'
end
factory :invalid_app, parent: :app do
name 'nil'
tos '0'
end
end
计数应更改为-1,但在删除时更改为0#销毁
应使用-on-POST#create进行渲染
应为:“更新的应用程序”
获得:“新应用”-更新
应为,但呈现为-on PUT#update
期望值:[#]
got:nil-on-GET#index
小事情-您的“http#u成功方法”是对完全相同的事情进行两次测试
你也可以在你的#before块后面放一个#let语句,以排除对应用程序的引用:
let(:app) { FactoryGirl.create(:app, developer: @developer) }
那么在你的规格里,就
it 'renders the :show view' do
get :show, id: app
expect_template(:show)
end
编辑:
然后操作顺序为1)在#before块中创建@developer,2)输入规范,3)在规范中第一次引用app
,let块将创建一个应用实例
这意味着您无法在#索引规范中考虑应用程序的创建,因为在这种情况下,规范将在创建应用程序之前调用操作。阅读您的代码,我想到了一些事情: 不需要在不带参数的方法调用中包含参数。只要
http\u成功
就行了
您应该尝试一致地使用现代RSpec期望语法。不要使用expect(assignments(:app)).to eq(app)
,而应使用expect(assignments(:app)).to eq(app)
。(有一个例外,那就是对mock的期望(即,should\u receive(:message)
),它将只采用现代的expect-to语法
对于控制器规格,我喜欢为实际调用该动作的每个动作创建一些小方法。您会注意到,您在get\show
规格中多次调用get:show,id:app
。要使规格更加干涸,您可以在description
块中编写以下方法:
def show!
get :show, id: app
end
尝试一致地使用一种散列语法。此外,Rails 4不能与Ruby 1.8一起运行,因此(几乎)没有理由使用散列火箭散列语法
如果我真的很挑剔,我通常把实例变量看作是一种气味。在几乎所有的情况下,实例变量都应该被重构成一个记忆化的<代码>让/<代码> < < /Cord>Bug >
如果我真的、真的、真的很挑剔,我更愿意把像你这样的控制器规格看作是严格意义上的控制器单元测试,而不是集成测试(这就是水豚的用途),因此,您根本不应该练习模型层。您应该只测试控制器是否正在向模型层发送正确的消息。换句话说,所有模型层内容都应该被删除。例如:describe 'GET #show' do
let(:app) { stub(:app) }
before do
App.stub(:find).and_return(app)
end
it 'populates @app' do
get :show, id: app
assigns(:app).should eq(app)
end
end
我知道这最后是个人偏好,不是形而上学的真理,甚至是一个广泛的标准惯例,所以你可以选择接受它或离开它。我更喜欢它,因为它保持了我的规格非常快,并且给了我一个非常明确的启发,当我的控制器动作做得太多时,我可能需要考虑重构。这可能是一个好习惯。首先,我不确定,但我怀疑你的
无效应用程序工厂可能是错误的。你的意思是
factory :invalid_app, parent: :app do
name nil
tos '0'
end
nil
作为rubyNilClass
而不是作为字符串的“nil”
至于其他关于清理工作的评论,以下是我的一些想法
对于每个description
,您可以使用before
块来避免对某些助手方法的需要和重复
describe 'GET #index' do
before do
get :index
end
it 'responds with an HTTP 200 status' do
http_success
end
it 'renders the :index view' do
expect_template(:index)
end
it 'populates @apps with the current_developers apps' do
expect(assigns(:app)).to eq([app])
end
end
还请注意,您不需要重新创建应用程序
,因为您的let
正在根据需要执行此操作
对于失败,我怀疑delete
计数更改可能失败,因为在预期范围内,测试框架正在创建一个新的应用程序(从let
)然后删除它,导致计数更改为0。对于该测试,您需要确保您的应用程序的创建超出您的预期。因为您使用的是let
,您可以这样做:
describe 'DELETE #destroy' do
it 'deletes the app' do
# ensure that app is already created
app
expect {
delete!
}.to change(App, :count).by(-1)
end
end
或者,将let
更改为let!
,这将在规范实际运行之前强制创建
至于其他失败,Think@DanielWright建议使用helper方法,我发现这些方法会使调试复杂化。例如,我看不出您在哪里将应用程序名称设置为“更新的应用程序”。也许更清晰的测试(针对特定的测试)不会使用helper方法,但可以更明确。例如
describe 'PUT #update' do
let(:app_attributes) { FactoryGirl.attributes_for(:app, name: 'The New App Name') }
before do
put :update, id: app, app: app_attributes
end
context 'with valid parameters' do
it 'locates the requested app' do
expect(assigns(:app)).to eq(app)
end
it 'changes app attributes' do
# notice the reload which will make sure you refetch this from the db
expect(app.reload.name).to eq('The New App Name')
end
it 'redirects to the updated app' do
expect(response).to redirect_to app
end
end
end
对于其他错误,您可能希望开始调试代码。您确定它应该工作吗?您查看了输出日志吗?可能测试正在那里工作,并在控制器代码中查找错误。您是否完成了调试的任何步骤?谢谢,我是否还必须在FactoryConfig的“应用程序”下添加“开发人员”,因为开发人员有很多应用程序,或者它的方式很好,而且对于“新”方法,我的控制器也是这样设置的“@app=current\u Developer.apps.new”,我如何才能为它添加一个测试?谢谢你,我修复了它,索引测试现在可以工作了,对上面的评论有什么建议吗?我不同意任何一个。谢谢你的建议,我重构了我拿出了一些方法来定义,并更新了语法。就存根测试而言,我不确定如何用我目前拥有的实现它们。此外,当我运行规范测试时,我通过了13次,失败了5次。我用我的完整代码更新了我的问题,并在底部显示了确切的错误,
describe 'DELETE #destroy' do
it 'deletes the app' do
# ensure that app is already created
app
expect {
delete!
}.to change(App, :count).by(-1)
end
end
describe 'PUT #update' do
let(:app_attributes) { FactoryGirl.attributes_for(:app, name: 'The New App Name') }
before do
put :update, id: app, app: app_attributes
end
context 'with valid parameters' do
it 'locates the requested app' do
expect(assigns(:app)).to eq(app)
end
it 'changes app attributes' do
# notice the reload which will make sure you refetch this from the db
expect(app.reload.name).to eq('The New App Name')
end
it 'redirects to the updated app' do
expect(response).to redirect_to app
end
end
end