From fe1b6aea538d05e5e703ee130e92ab6a13152bc7 Mon Sep 17 00:00:00 2001 From: Vincent Landgraf Date: Tue, 1 Aug 2017 13:58:03 +0200 Subject: [PATCH 1/3] implements system-out and system-err output formating using example-metadata --- example/spec/example_spec.rb | 6 ++++++ example/spec/spec_helper.rb | 15 +++++++++++++++ lib/rspec_junit_formatter.rb | 15 +++++++++++++++ lib/rspec_junit_formatter/rspec2.rb | 8 ++++++++ lib/rspec_junit_formatter/rspec3.rb | 8 ++++++++ spec/rspec_junit_formatter_spec.rb | 17 ++++++++++++----- 6 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 example/spec/spec_helper.rb diff --git a/example/spec/example_spec.rb b/example/spec/example_spec.rb index 60360df..da25204 100644 --- a/example/spec/example_spec.rb +++ b/example/spec/example_spec.rb @@ -1,3 +1,4 @@ +require "spec_helper" require_relative "shared_examples" describe "some example specs" do @@ -42,4 +43,9 @@ end it_should_behave_like "shared examples" + + it "can capture stdout and stderr" do + $stdout.puts "Test" + $stderr.puts "Bar" + end end diff --git a/example/spec/spec_helper.rb b/example/spec/spec_helper.rb new file mode 100644 index 0000000..254be61 --- /dev/null +++ b/example/spec/spec_helper.rb @@ -0,0 +1,15 @@ +RSpec.configure do |config| + # register around filter that captures stderr and stdout + config.around(:each) do |example| + $stdout = StringIO.new + $stderr = StringIO.new + + example.run + + example.metadata[:stdout] = $stdout.string + example.metadata[:stderr] = $stderr.string + + $stdout = STDOUT + $stderr = STDERR + end +end diff --git a/lib/rspec_junit_formatter.rb b/lib/rspec_junit_formatter.rb index 418f207..99edda9 100644 --- a/lib/rspec_junit_formatter.rb +++ b/lib/rspec_junit_formatter.rb @@ -71,9 +71,24 @@ def xml_dump_example(example) output << %{ time="#{escape("%.6f" % duration_for(example))}"} output << %{>} yield if block_given? + xml_dump_output(example) output << %{\n} end + def xml_dump_output(example) + if (stdout = stdout_for(example)) && !stdout.empty? + output << %{} + output << escape(stdout) + output << %{} + end + + if (stderr = stderr_for(example)) && !stderr.empty? + output << %{} + output << escape(stderr) + output << %{} + end + end + # Inversion of character range from https://www.w3.org/TR/xml/#charsets ILLEGAL_REGEXP = Regexp.new( "[^" << diff --git a/lib/rspec_junit_formatter/rspec2.rb b/lib/rspec_junit_formatter/rspec2.rb index 7084e3f..c9c51b1 100644 --- a/lib/rspec_junit_formatter/rspec2.rb +++ b/lib/rspec_junit_formatter/rspec2.rb @@ -68,4 +68,12 @@ def find_shared_group(example) def group_and_parent_groups(example) example.example_group.parent_groups + [example.example_group] end + + def stdout_for(example) + example.metadata[:stdout] + end + + def stderr_for(example) + example.metadata[:stderr] + end end diff --git a/lib/rspec_junit_formatter/rspec3.rb b/lib/rspec_junit_formatter/rspec3.rb index bf383ff..736eff5 100644 --- a/lib/rspec_junit_formatter/rspec3.rb +++ b/lib/rspec_junit_formatter/rspec3.rb @@ -99,6 +99,14 @@ def strip_diff_colors(string) # string.sub(STRIP_DIFF_COLORS_BLOCK_REGEXP) { |match| match.gsub(STRIP_DIFF_COLORS_CODES_REGEXP, "".freeze) } end + + def stdout_for(example) + example.metadata[:stdout] + end + + def stderr_for(example) + example.metadata[:stderr] + end end # rspec-core 3.0.x forgot to mark this as a module function which causes: diff --git a/spec/rspec_junit_formatter_spec.rb b/spec/rspec_junit_formatter_spec.rb index 8b52310..67be879 100644 --- a/spec/rspec_junit_formatter_spec.rb +++ b/spec/rspec_junit_formatter_spec.rb @@ -13,7 +13,7 @@ let(:testsuite) { doc.xpath("/testsuite").first } let(:testcases) { doc.xpath("/testsuite/testcase") } - let(:successful_testcases) { doc.xpath("/testsuite/testcase[count(*)=0]") } + let(:successful_testcases) { doc.xpath("/testsuite/testcase[not(failure) and not(skipped)]") } let(:pending_testcases) { doc.xpath("/testsuite/testcase[skipped]") } let(:failed_testcases) { doc.xpath("/testsuite/testcase[failure]") } let(:shared_testcases) { doc.xpath("/testsuite/testcase[contains(@name, 'shared example')]") } @@ -29,7 +29,7 @@ expect(testsuite).not_to be(nil) expect(testsuite["name"]).to eql("rspec") - expect(testsuite["tests"]).to eql("11") + expect(testsuite["tests"]).to eql("12") expect(testsuite["skipped"]).to eql("1") expect(testsuite["failures"]).to eql("8") expect(testsuite["errors"]).to eql("0") @@ -39,7 +39,7 @@ # it has some test cases - expect(testcases.size).to eql(11) + expect(testcases.size).to eql(12) testcases.each do |testcase| expect(testcase["classname"]).to eql("spec.example_spec") @@ -49,11 +49,14 @@ # it has successful test cases - expect(successful_testcases.size).to eql(2) + expect(successful_testcases.size).to eql(3) successful_testcases.each do |testcase| expect(testcase).not_to be(nil) - expect(testcase.children).to be_empty + # test results that capture stdout / stderr are not 'empty' + unless (testcase["name"]) =~ /capture stdout and stderr/ + expect(testcase.children).to be_empty + end end # it has pending test cases @@ -117,6 +120,10 @@ # it correctly escapes reserved xml characters expect(doc.xpath("//testcase[contains(@name, 'html')]").first[:name]).to eql(%{some example specs escapes }) + + # it correctly captures stdout / stderr output + expect(doc.xpath("//testcase/system-out").text).to eql("Test\n") + expect(doc.xpath("//testcase/system-err").text).to eql("Bar\n") end context "when $TEST_ENV_NUMBER is set" do From 4d582a6f4537b7d3c2e32edc9b02acd4084f69ea Mon Sep 17 00:00:00 2001 From: Vincent Landgraf Date: Tue, 1 Aug 2017 14:21:37 +0200 Subject: [PATCH 2/3] fixes the reporting for rspec 3.x --- lib/rspec_junit_formatter/rspec3.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rspec_junit_formatter/rspec3.rb b/lib/rspec_junit_formatter/rspec3.rb index 736eff5..f11a57c 100644 --- a/lib/rspec_junit_formatter/rspec3.rb +++ b/lib/rspec_junit_formatter/rspec3.rb @@ -100,12 +100,12 @@ def strip_diff_colors(string) string.sub(STRIP_DIFF_COLORS_BLOCK_REGEXP) { |match| match.gsub(STRIP_DIFF_COLORS_CODES_REGEXP, "".freeze) } end - def stdout_for(example) - example.metadata[:stdout] + def stdout_for(example_notification) + example_notification.example.metadata[:stdout] end - def stderr_for(example) - example.metadata[:stderr] + def stderr_for(example_notification) + example_notification.example.metadata[:stderr] end end From 49195d870850fc63622f6ee3cb3f628cd5870721 Mon Sep 17 00:00:00 2001 From: Samuel Cochran Date: Sat, 26 May 2018 20:08:57 +1000 Subject: [PATCH 3/3] Document stream capture and reporting --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 6cfe960..2cc9a7b 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,30 @@ For use with `parallel_tests`, add `$TEST_ENV_NUMBER` in the output file option The formatter includes `$TEST_ENV_NUMBER` in the test suite name within the XML, too. +### Capturing output + +If you like, you can capture the standard output and error streams of each test into the `:stdout` and `:stderr` example metadata which will be added to the junit report, e.g.: + +```ruby +# spec_helper.rb + +RSpec.configure do |config| + # register around filter that captures stdout and stderr + config.around(:each) do |example| + $stdout = StringIO.new + $stderr = StringIO.new + + example.run + + example.metadata[:stdout] = $stdout.string + example.metadata[:stderr] = $stderr.string + + $stdout = STDOUT + $stderr = STDERR + end +end +``` + ## Caveats * XML can only represent a [limited subset of characters][xml-charsets] which