在Rails中创建一些JavaScript功能时,我使用了一些丑陋的Capybara规范来帮助我确保一切正常。其中一个规格如下:
it 'creates a report document', js: true do
visit new_project_report_document_path @project
expect(page).to have_active_navigation_items 'Projects'
expect(page).to have_breadcrumbs 'A4AA 2.0', 'Projects', 'Project test name', 'Reports', 'Create'
expect(page).to have_headline 'Create Report'
expect {
select 'Template 1', from: 'report_report_template_id'
}.to change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}']", visible: true }.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_name[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_description[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_intro[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_content[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_outro[disabled]", visible: false}.from(true)
expect(page).to have_css "[data-template-inputs-id='#{@report_template_1.id}'] #report_name", text: @report_template_1.name
expect(page).to have_css "[data-template-inputs-id='#{@report_template_1.id}'] #report_description", text: ''
expect(page).to have_css "[data-template-inputs-id='#{@report_template_1.id}'] #report_intro", text: @report_template_1.intro
expect(page).to have_css "[data-template-inputs-id='#{@report_template_1.id}'] #report_content", text: @report_template_1.content
expect(page).to have_css "[data-template-inputs-id='#{@report_template_1.id}'] #report_outro", text: @report_template_1.outro
expect {
select 'Template 2', from: 'report_report_template_id'
}.to change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}']", visible: true }.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_name[disabled]", visible: false}.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_description[disabled]", visible: false}.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_intro[disabled]", visible: false}.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_content[disabled]", visible: false}.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_1.id}'] #report_outro[disabled]", visible: false}.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_2.id}']", visible: true }.from(false)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_2.id}'] #report_name[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_2.id}'] #report_description[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_2.id}'] #report_intro[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_2.id}'] #report_content[disabled]", visible: false}.from(true)
.and change { page.has_css? "[data-template-inputs-id='#{@report_template_2.id}'] #report_outro[disabled]", visible: false}.from(true)
expect(page).to have_css "[data-template-inputs-id='#{@report_template_2.id}'] #report_name", text: @report_template_2.name
expect(page).to have_css "[data-template-inputs-id='#{@report_template_2.id}'] #report_description", text: ''
expect(page).to have_css "[data-template-inputs-id='#{@report_template_2.id}'] #report_intro", text: @report_template_2.intro
expect(page).to have_css "[data-template-inputs-id='#{@report_template_2.id}'] #report_content", text: @report_template_2.content
expect(page).to have_css "[data-template-inputs-id='#{@report_template_2.id}'] #report_outro", text: @report_template_2.outro
fill_in 'report_name', with: 'newname'
fill_in 'report_description', with: 'newdescription'
fill_in 'report_intro', with: 'newintro'
fill_in 'report_content', with: 'newcontent'
fill_in 'report_outro', with: 'newoutro'
click_button 'Create Report'
expect(page).to have_flash 'Report was successfully created.'
end
我知道这是一个丑陋的规范,但我注意到它需要花费大量时间:大约40秒!
Finished in 41.86 seconds (files took 0.44731 seconds to load)
这是另一个已激活JavaScript的规范。虽然它要小得多(并且只发生一次整页加载),但我认为它需要的时间不成比例地小于上面的那个:
it 'allows to remove an existing finding', js: true, focus: true do
visit edit_project_boilerplate_copy_path(@boilerplate_copy.project, @boilerplate_copy)
click_link 'Remove finding'
expect {
click_button 'Update Boilerplate'
} .to change { Finding.count }.by -1
end
需要大约6-7秒:
Finished in 6.62 seconds (files took 0.52104 seconds to load)
所以我想知道为什么第一个需要这么多时间。我的完整规格套件大约有400个规格并花费了大约一分钟,因此通过添加新规格,它增加到将近2分钟!这是不可接受的。
那么:我的JS规范如何改进?它与许多visible: false
语句有关吗?还是很多and change { ... }
的东西?也许两者都在互动?
删除其中包含visible: false
的所有行时,速度会快得多:
Finished in 6.39 seconds (files took 0.41315 seconds to load)
我在OSX El Capitan上使用了capybara(2.5.0),rspec(3.3.0),rails(4.2.1)和poltergeist(1.7.0)以及phantomjs(1.9.8)。
答案 0 :(得分:2)
你的测试是如此缓慢,因为你正在进入Capybaras等待行为。 #has_css?/have_css
将等待Capybara.default_max_wait_time秒,以便匹配元素显示在页面上,然后回复,如果不是,则返回false。当你希望某个元素不在页面上时你想要使用#has_no_css?/ have_no_css(或者not_to #has_css?/ have_css,因为它们最终是同一个东西),因为一旦找不到元素,它就会返回
has_css?(....) #will wait until element appears or default_max_wait_time
has_no_css?(....) #will wait until element is gone or default_max_wait_time
基本上在你的情况下,你不想使用带有has_css的change
匹配器?因为它不允许您在动作的每一侧使用尽快匹配的方法。如果你真的想保留change
匹配器,可能的选择是将一个小的等待值传递给has_css? wait: 0.5
或者会减少该项目的最长等待时间的东西,但可能需要进行调整以允许页面上发生的任何操作实际完成