We have written a large number of selenium UI tests that very occasionally fail due to dumb reasons like a web server being too slow. Additionally, we make heavy use of rspec tags to only run the tests that we want to run. We want to retry ONLY the tests that are tagged with something AND that have previously failed. In the following example, running rspec --tag all --only_failures
runs the last two tests, when I only want it to run the 2nd test.
spec_helper.rb
RSpec.configure do |config|
config.example_status_persistence_file_path = "RunResults.txt"
end
test_spec.rb
require 'rspec'
require 'spec_helper'
require './test'
RSpec.describe Test do
it "should pass", :all, :one do
expect(1).to eql(1)
end
it "should fail", :all, :two do
expect(1).to eql(2)
end
it "should fail", :three do
expect(1).to eql(2)
end
end
RunResults.txt
example_id | status | run_time |
------------------------ | ------ | --------------- |
./spec/test_spec.rb[1:1] | passed | 0.00055 seconds |
./spec/test_spec.rb[1:2] | failed | 0.01796 seconds |
./spec/test_spec.rb[1:3] | failed | 0.00017 seconds |
RSpec
$ rspec --tag all --only_failures
Run options: include {:all=>true, :last_run_status=>"failed"}
.FF
Failures:
1) Test should fail
Failure/Error: expect(1).to eql(2)
expected: 2
got: 1
(compared using eql?)
# ./spec/test_spec.rb:11:in `block (2 levels) in <top (required)>'
2) Test should fail
Failure/Error: expect(1).to eql(2)
expected: 2
got: 1
(compared using eql?)
# ./spec/test_spec.rb:15:in `block (2 levels) in <top (required)>'
Finished in 0.01949 seconds (files took 0.24733 seconds to load)
3 examples, 2 failures
Failed examples:
rspec ./spec/test_spec.rb:10 # Test should fail
rspec ./spec/test_spec.rb:14 # Test should fail
When you run:
rspec --tag all --only_failures
all three tests get selected: 3 examples, 2 failures
. So it's running failed tests + tests tagged all
.
I hope I didn't miss an obvious command for doing this. Best I could come up is a little monkey patch to save tags into rspec persistence file:
config.example_status_persistence_file_path = "tmp/spec_persistence.txt"
require "rspec"
require "spec_helper"
# NOTE: override the way persistence file is saved;
# just put this somewhere appropriate
module RSpec
module Core
class ExampleStatusPersister
private
def statuses_from_this_run
@examples.map do |ex|
result = ex.execution_result
# NOTE: collect all the tags;
# maybe there is a better way, i haven't searched too hard
tags = ex.metadata.except(*[:block, :description_args, :description, :full_description, :described_class, :file_path, :line_number, :location, :absolute_file_path, :rerun_file_path, :scoped_id, :execution_result, :example_group, :shared_group_inclusion_backtrace, :last_run_status])
{
:example_id => ex.id,
:status => result.status ? result.status.to_s : Configuration::UNKNOWN_STATUS,
:run_time => result.run_time ? Formatters::Helpers.format_duration(result.run_time) : "",
# NOTE: save tags for reference later
:tags => tags.keys.join(","),
}
end
end
end
end
end
RSpec.describe "Test" do
it("1 should pass", :all, :one) { expect(1).to eql(1) }
it("2 should fail", :all, :two) { expect(1).to eql(2) }
it("3 should fail", :three) { expect(1).to eql(2) }
end
https://github.com/rspec/rspec-core/blob/v3.12.0/lib/rspec/core/example_status_persister.rb#L46
When you run tests now, all tags are saved into persistence file:
$ cat tmp/spec_persistence.txt
example_id | status | run_time | tags |
------------------ | ------ | --------------- | ------- |
./spec/one.rb[1:1] | passed | 0.00075 seconds | one,all |
./spec/one.rb[1:2] | failed | 0.01538 seconds | two,all |
./spec/one.rb[1:3] | failed | 0.00021 seconds | three |
This is a quick script to select the appropriate tests:
# ./rspec_runner
#!/usr/bin/env ruby
require "rspec"
tag = ARGV[0]
# https://github.com/rspec/rspec-core/blob/v3.12.0/lib/rspec/core/example_status_persister.rb#L8
results = RSpec::Core::ExampleStatusPersister.load_from("tmp/spec_persistence.txt")
filtered = results.filter_map do |result|
result[:example_id] if result[:status] == "failed" && result[:tags].split(",").include?(tag)
end
if filtered.empty?
puts "No failed tests found for tag: #{tag}"
else
puts "Running failed tests with tag: #{tag}"
puts "rspec #{filtered.join(' ')}"
puts
# NOTE: run tests by example_id
system "rspec #{filtered.join(' ')}"
end
Drumroll:
$ ./rspec_runner all
Running failed tests with tag: all
rspec ./spec/one.rb[1:2]
Run options: include {:ids=>{"./spec/one.rb"=>["1:2"]}}
F
# ...
1 example, 1 failure
Failed examples:
rspec ./spec/one.rb:31 # Test 2 should fail
$ ./rspec_runner one
No failed tests found for tag: one
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.