-
Notifications
You must be signed in to change notification settings - Fork 22
Add feature to generate JUnit XML output #720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/spoom/cli/srb/tc.rb
Outdated
if errors.empty? | ||
# Avoid creating an empty report when there are no errors so that | ||
# reporting tools know that the type checking ran successfully. | ||
testcase_element = testsuite_element.add_element("testcase") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this is happening in both branches, we can extract it before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other's in a loop, but this one isn't, so I don't think that will work?
lib/spoom/cli/srb/tc.rb
Outdated
) | ||
else | ||
errors.each do |error| | ||
testcase_element = testsuite_element.add_element("testcase") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think moving this to Error
there: https://github.com/Shopify/spoom/blob/main/lib/spoom/sorbet/errors.rb#L118 to error.to_junit_xml_element
returns a REXML::Element
?
You can also move the write_errors_to_xml
to the Errors
module: https://github.com/Shopify/spoom/blob/main/lib/spoom/sorbet/errors.rb#L6 maybe renamed as to_junit_xml
lib/spoom/cli/srb/tc.rb
Outdated
def write_errors_to_xml(errors, path:) | ||
doc = REXML::Document.new | ||
doc << REXML::XMLDecl.new | ||
testsuite_element = doc.add_element("testsuite") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just make it a REXML::Element.new("testsuite")
so you don't have to create the doc for all the method and only create it at the end:
testsuite = REXML::Element.new("testsuite")
if errors.empty?
#...
else
errors.each { |error| testsuite << error.to_junit_xml_element }
end
doc = REXML::Document.new
doc << REXML::XMLDecl.new
doc << testsuite
@@ -2,6 +2,7 @@ | |||
# frozen_string_literal: true | |||
|
|||
require "test_with_project" | |||
require "rexml/document" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REXML
isn't included by default, AFAICT, and if we remove this line we get errors running the tests.
test/spoom/cli/srb/tc_test.rb
Outdated
Tempfile.create("sorbet_junit") do |tempfile| | ||
result = @project.spoom("srb tc --junit_output_path=#{tempfile.path}") | ||
expected_doc = <<~XML.chomp | ||
<?xml version='1.0'?> | ||
<testsuite name='Sorbet' failures='0'> | ||
<testcase name='Typecheck' tests='1'/> | ||
</testsuite> | ||
XML | ||
tempfile.rewind | ||
actual_doc = tempfile.read | ||
assert_equal(expected_doc, actual_doc) | ||
assert(result.status) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@project
is already a temporary context, you can do something like this:
Tempfile.create("sorbet_junit") do |tempfile| | |
result = @project.spoom("srb tc --junit_output_path=#{tempfile.path}") | |
expected_doc = <<~XML.chomp | |
<?xml version='1.0'?> | |
<testsuite name='Sorbet' failures='0'> | |
<testcase name='Typecheck' tests='1'/> | |
</testsuite> | |
XML | |
tempfile.rewind | |
actual_doc = tempfile.read | |
assert_equal(expected_doc, actual_doc) | |
assert(result.status) | |
end | |
result = @project.spoom("srb tc --junit_output_path=junit.xml") | |
assert_equal(<<~XML.chomp, @project.read("junit.xml")) | |
<?xml version='1.0'?> | |
<testsuite name='Sorbet' failures='0'> | |
<testcase name='Typecheck' tests='1'/> | |
</testsuite> | |
XML | |
assert(result.status) |
test/spoom/cli/srb/tc_test.rb
Outdated
end | ||
|
||
def test_output_errors_to_junit_xml | ||
Tempfile.create("sorbet_junit") do |tempfile| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, we don't need the Tempfile
@Morriar I think I've responded to all of your feedback; I usually leave clicking "Resolve" for the reviewer so they can confirm the change first; let me know if you have a different preference. |
This is particularly useful when integrating Sorbet into CI builds running on Jenkins so that Jenkins can display the output as specific failures.
Co-authored-by: Alexandre Terrasa <[email protected]>
Background
It's common to use the JUnit plugin on Jenkins to consume machine-readable output about the tests in your CI builds. For example, on our Ruby app tests we use the rspec_junit_formatter to output results from RSpec in a format that Jenkins can consume.
What
This PR adds JUnit style XML output as an option to Spoom so that running Sorbet in CI on Jenkins can output error information that can be displayed natively by Jenkins (and then, in turn, passed along to e.g. Github's interface).
With these changes Sorbet failures on a build look like this:
and a clean Sorbet build looks like this:
TODO
I'm happy to add docs for this feature as well, but I wanted to get initial review of the feature going sooner rather than later.