2015-12-21 75 views
0

我使用Rspec和fixtures来测试Rails模型。使用Fixtures在rspec测试中删除重复的代码

一些测试用例有非常相似的代码看起来像这样

it 'creates request' do 
    u = users(:user_one) 
    o = documents(:document_one) 
    requests = Request.where(status: Request::PENDING, user: u, document: o) 
    expect { o.request_access(u) }.to change { requests.count }.by(1)  
end 

it 'does not create a request if user already has access' do 
    u = users(:user_one) 
    o = documents(:document_one) 
    Request.create status: Request::ACCEPTED, user: u, document: 0 
    requests = Request.where(status: Request::PENDING, user: u, document: o) 
    expect { o.request_access(u) }.not_to change { requests.count }  
end 

我怎么能消除这里代码重复?有没有办法使用let来解决问题?如果我有更多的测试用例,其中使用了其他固件,那么Request.where(status: Request::PENDING, user: u, document: o)是指其他对象呢?

回答

4

这应该是干的不够。 let所有的事情!

let(:user) { users(:user_one) } 
let(:document) { documents(:document_one) } 
let(:requests) { Request.where(status: Request::PENDING, user: user, document: document) } 
let(:action) { document.request_access(user) } 

context 'user does not have access' do 
    it 'creates request' do 
    expect { action }.to change { requests.count }.by(1)  
    end 
end 

context 'user has approved access' do 
    before do 
    Request.create status: Request::ACCEPTED, user: user, document: document 
    end 

    it 'does not create a request' do 
    expect { action }.not_to change { requests.count }  
    end 
end 
+0

伟大,谢谢。出于某种原因,我相信你不能在其他'let'块中使用'let'块。使用'subject'作为':action'也不是更清楚吗? –

+0

@ S.S.J:不知道。从不喜欢'主题'。此外,它暗示了一些对象,而在这里我们有一个行动。但无论你想要什么都可以命名。 :) –

+0

样式不同,但除非您正在编写共享方法,否则使用明确的“主题”通常是不鼓励的。理想情况下,从阅读测试中可以明显看出这个问题。 – zetetic

1

你可以使用letbefore(:each)

before(:each) do 
    @u = users(:user_one) 
    @o = documents(:document_one) 
end 

it 'creates request' do 
    requests = Request.where(status: Request::PENDING, user: @u, document: @o) 
    expect { @o.request_access(@u) }.to change { requests.count }.by(1)  
end 

it 'does not create a request if user already has access' do 
    Request.create status: Request::ACCEPTED, user: @u, document: @o 
    requests = Request.where(status: Request::PENDING, user: @u, document: @o) 
    expect { @o.request_access(@u) }.not_to change { requests.count }  
end 

let(:u) { users(:user_one) } 
let(:o) { documents(:document_one) } 

it 'creates request' do 
    requests = Request.where(status: Request::PENDING, user: u, document: o) 
    expect { o.request_access(u) }.to change { requests.count }.by(1)  
end 

it 'does not create a request if user already has access' do 
    Request.create status: Request::ACCEPTED, user: u, document: o 
    requests = Request.where(status: Request::PENDING, user: u, document: o) 
    expect { o.request_access(u) }.not_to change { requests.count }  
end 

编辑:您不能requests查询过滤器之前添加到,因为你之前有Request.create你在第二个测试中查询。无论如何,要干它,你可以创建一个方法,返回查询:

let(:u) { users(:user_one) } 
let(:o) { documents(:document_one) } 

it 'creates request' do 
    requests 
    expect { o.request_access(u) }.to change { requests.count }.by(1)  
end 

it 'does not create a request if user already has access' do 
    Request.create status: Request::ACCEPTED, user: u, document: o 
    requests 
    expect { o.request_access(u) }.not_to change { requests.count }  
end 

def requests 
    Request.where(status: Request::PENDING, user: u, document: o) 
end 
+0

我真的很想删除'request = Request.where(status:Request :: PENDING,user:u,document:o)'。有没有办法做到这一点? –

+0

问题是你需要在请求查询之前调用'Request.create',所以你不能将它添加到'before(:each)'...请注意我编辑的答案 – gabrielhilal

+0

如果你有' Request.create'在before操作中会在两个测试中运行。所以,你的第一次测试会失败,除非你把测试包装在像Sergio回答 – gabrielhilal