Can I eliminate multiple within_frame calls when writing rspec tests for my site? - ruby

I'm writing tests for a legacy app using Capybara + Rspec, and I'm finding that all of my tests need to be run within a specific frame. I've written up a little rspec helper that I run before every test to confirm I'm getting the session set correctly, and then visit the URL:
def set_session
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(
app,
window_size: [1280, 1024] ,
timeout: 60
)
end
Capybara.javascript_driver = :poltergeist
#sess = Capybara::Session.new :poltergeist
#authentication stuff here...
end
describe 'initiate_requests' do
before(:all) do
set_session
end
before(:each) do
#sess.visit(URL)
end
it 'shows error if not degree seeking student' do
#sess.within_frame(FRAME) {
set_test_vars(FAILID)
expect(#sess).to have_selector("#NO_PROGRAM_ERROR")
expect(#sess).not_to have_selector("#submitButton")
}
end
it 'works for a degree seeking student' do
#sess.within_frame(FRAME) {
set_test_vars(VALIDID)
expect(#sess).not_to have_selector("#NO_PROGRAM_ERROR")
expect(#sess).to have_selector("#submitButton")
}
end
end
This works just fine. However, every single test I need to write in the form:
it 'works for a degree seeking student' do
#sess.within_frame(FRAME) {
#do tests..
}
end
Is there some way I can in reset the #sess to just work within the frame all the time? Lacking that, is there some way I can declare the #sess.within_frame(FRAME) just once and have all of the tests follow that declaration?

I haven't specifically tried it, but you can probably use an around hook rather than your before(:each) hook
around(:each) do |example|
#sess.visit(URL)
#sess.within_frame(FRAME) do
example.run
end
end

Related

Run cleanup step if any it block failed

When one of my it blocks fails, I want to run a cleanup step. When all of the it blocks succeed I don't want to run the cleanup step.
RSpec.describe 'my describe' do
it 'first it' do
logic_that_might_fail
end
it 'second it' do
logic_that_might_fail
end
after(:all) do
cleanup_logic if ONE_OF_THE_ITS_FAILED
end
end
How do I implement ONE_OF_THE_ITS_FAILED?
Not sure if RSpec provides something out of the box, but this would work:
RSpec.describe 'my describe' do
before(:all) do
#exceptions = []
end
after(:each) do |example|
#exceptions << example.exception
end
after(:all) do |a|
cleanup_logic if #exceptions.any?
end
# ...
end
I digged a little into the RSpec Code and found a way to monkey patch the RSpec Reporter class. Put this into your spec_helper.rb:
class RSpecHook
class << self
attr_accessor :hooked
end
def example_failed(example)
# Code goes here
end
end
module FailureDetection
def register_listener(listener, *notifications)
super
return if ::RSpecHook.hooked
#listeners[:example_failed] << ::RSpecHook.new
::RSpecHook.hooked = true
end
end
RSpec::Core::Reporter.prepend FailureDetection
Of course it gets a little more complex if you wish to execute different callbacks depending on the spec you're running at the moment.
Anyway, this way you do not have to mess up your testing code with exceptions or counters to detect failures.

RSpec: Always execute before(:all) in begin/rescue

I'm writing Selenium tests, using Watir-Webdriver and RSpec, which can be a bit spotty when they're first being developed. I've run into a situation where I want to create something on the UI in before :all, however it can throw exceptions (based on timing or poor loading). When that happens I want to take a screenshot.
Here's what I have:
RSpec.configure do |config|
config.before(:all) do |group| #ExampleGroup
#browser = Watir::Browser.new $BROWSER
begin
yield #Fails on yield, there is no block
rescue StandardError => e
Utilities.create_screenshot(#browser)
raise(e)
end
end
end
I run it and get an error:
LocalJumpError: no block given (yield)
The reason I assumed yielding would work is RSpec's definition of before:
def before(*args, &block)
hooks.register :append, :before, *args, &block
end
How can I wrap the code I've put in my before :all in a begin/rescue block without having to do it on every suite?
Thanks in advanced.
The code you've written in the before hook is the &block you're referring to in RSpec::Hooks#before. The hook yields to your code, then runs your tests after the yield is complete.
As for how to make this work, I think this should do:
RSpec.configure do |config|
# ensures tests are run in order
config.order = 'defined'
# initiates Watir::Browser before all tests
config.before(:all) do
#browser = Watir::Browser.new $BROWSER
end
# executes Utilities.create_screenshot if an exception is raised by RSpec
# and the test is tagged with the :first metadata
config.around(:each) do |example|
example.run
Utilities.create_screenshot(#browser) if example.exception && example.metadata[:first]
end
end
This configuration requires the first test be tagged with metadata:
describe Thing, :first do
it "does something" do
# ...
end
end
This way, you'll only take a screenshot at the beginning of your run for a failing test, and not after every failing test. If you'd rather not mess with metadata (or prefer your tests are run in random order), you could do something like this:
RSpec.configure do |config|
# initiates Watir::Browser before all tests
config.before(:all) do
#test_count = 0
#browser = Watir::Browser.new $BROWSER
end
# executes Utilities.create_screenshot if an exception is raised by RSpec
# and the test is the first to run
config.around(:each) do |example|
#test_count += 1
example.run
Utilities.create_screenshot(#browser) if example.exception && #test_count == 1
end
end
This works for me. Instead of begin/rescue in the before :all hook,
config.after :each do
example_exceptions = []
RSpec.world.example_groups.each do |example_group|
example_group.examples.each do |example|
example_exceptions << !example.exception.nil?
end
end
has_exceptions = example_exceptions.any? {|exception| exception}
#Handle if anything has exceptions
end

Change default Capybara browser window size

So, with respect to integration testing using Capybara and RSpec, I know I can do this:
page.driver.browser.manage.window.resize_to(x,y)
per How to set Browser Window size in Rspec (Selenium) for specific RSpec tests, but is there a way to do this globally so that every test that is affected by media queries doesn't have to define this?
A proper way to do it for all js tests is to add following inside spec_helper.rb RSpec.configure block
config.before(:each, js: true) do
Capybara.page.driver.browser.manage.window.maximize
end
to maximize the window. Change to resize_to(x,y) to set any window size.
EDIT: If you happen to be using Poltergeist the correct way to do it is
config.before(:each, js: true) do
Capybara.page.driver.browser.resize(x,y)
end
You could define that under before(:all)
describe "Test" do
before(:all) do
...
...
page.driver.browser.manage.window.resize_to(x,y) #Mention it here
end
it "should find everything" do
...
end
after(:all) do
...
end
end
Perhaps due to a recent change in Capybara, what worked for me was:
before do
Capybara.page.current_window.resize_to(x, y)
end
For test runtime in Capybara 2.2.4 version you can achieve this by doing
before do
handle = Capybara.page.driver.current_window_handle
Capybara.page.driver.resize_window_to(handle, width, height)
end
Or
before do
Capybara.page.current_window.resize_to(width, height)
end
If you get Capybara::NotSupportedByDriverError: Capybara::Driver::Base#current_window_handle YOU MUST CHANGE YOUR DRIVER FOR EXAMPLE USE JAVASCRIPT DRIVER!
before do
Capybara.page.current_window.resize_to(width, height)
end
scenario js: true do
# your test here
end
#tirdadc if you're using Poltergeist, you can add something like this to your rails_helper.rb file:
Capybara.register_driver :poltergeist do |app|
options = {
# js_errors: true,
# cookies: true,
window_size: [320, 568] # iphone 5
}
Capybara::Poltergeist::Driver.new(app, options)
end

Model not being saved using Selenium but saved using Rack::Test

I have the following test that works in Rack::Test but not using Selenium. I.e. if I add , js: true to the describe block, I get an error message in Firefox saying that it couldn't find the License with id=(the id of #l)
describe "should hide allocation rule # for pdf & clickthrough licenses" do
it "reads current state and shows/hides fields appropriately" do
#l = FactoryGirl.create(:license:,
way: License::CLICK_WAY)
visit edit_admin_license_path(#l)
end
end
Why? I must be missing something. I can verify with Sequel Pro that the record is not getting saved when using js: true.
I need this spec to run in Selenium because I have javascript to test.
The simple solution was to turn transactional fixtures off.
Why does my Cucumber test fail when run with Selenium?
in spec/spec_helper.rb:
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before :each do
if Capybara.current_driver == :rack_test
DatabaseCleaner.strategy = :transaction
else
DatabaseCleaner.strategy = :truncation
end
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
end
and in the Gemfile, test section
gem 'database_cleaner'

Performance testing with RSpec

I'm trying to incorporate performance tests into a test suite for non-Rails app and have a couple problems.
I don't need to run perf tests every time, how can I exclude them? Commenting and uncommenting config.filter_run_excluding :perf => true seems like a bad idea.
How do I report benchmark results? I think RSpec has some mechanism for that.
I created rspec-benchmark Ruby gem for writing performance tests in RSpec. It has many expectations for testing speed, resources usage, and scalability.
For example, to test how fast your code is:
expect { ... }.to perform_under(60).ms
Or to compare with another implementation:
expect { ... }.to perform_faster_than { ... }.at_least(5).times
Or to test computational complexity:
expect { ... }.to perform_logarithmic.in_range(8, 100_000)
Or to see how many objects get allocated:
expect {
_a = [Object.new]
_b = {Object.new => 'foo'}
}.to perform_allocation({Array => 1, Object => 2}).objects
To filter your tests you can separate the specs into a performance directory and add a rake task
require 'rspec/core/rake_task'
desc 'Run performance specs'
RSpec::Core::RakeTask.new(:perf) do |task|
task.pattern = 'spec/performance{,/*/**}/*_spec.rb'
end
Then run them whenever you need them:
rake perf
First problem partially solved and second problem solved completely with this piece of code in spec/spec_helper.rb
class MessageHelper
class << self
def messages
#messages ||= []
end
def add(msg)
messages << msg
end
end
end
def message(msg)
MessageHelper.add msg
end
RSpec.configure do |c|
c.filter_run_excluding :perf => !ENV["PERF"]
c.after(:suite) do
puts "\nMessages:"
MessageHelper.messages.each {|m| puts m}
end
end

Resources