Skip to content
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

fix(cli): output more detailed information for steps when using JUnit reporter #22797

Merged
merged 29 commits into from
Mar 25, 2024

Conversation

magurotuna
Copy link
Member

@magurotuna magurotuna commented Mar 8, 2024

This patch gets JUnit reporter to output more detailed information for test steps (subtests).

Issue with previous implementation

In the previous implementation, the test hierarchy was represented using several XML tags like the following:

  • <testsuites> corresponds to the entire test (one execution of deno test has exactly one <testsuites> tag)
  • <testsuite> corresponds to one file, such as main_test.ts
  • <testcase> corresponds to one Deno.test(...)
  • <property> corresponds to one t.step(...)

This structure describes the test layers but one problem is that <property> tag is used for any use cases so some tools that can ingest a JUnit XML file might not be able to interpret <property> as subtests.

How other tools address it

Some of the testing frameworks in the ecosystem address this issue by fitting subtests into the <testcase> layer. For instance, take a look at the following Go test file:

package main_test

import "testing"

func TestMain(t *testing.T) {
  t.Run("child 1", func(t *testing.T) {
    // OK
  })

  t.Run("child 2", func(t *testing.T) {
    // Error
    t.Fatal("error")
  })
}

Running gotestsum, we can get the output like this:

<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="3" failures="2" errors="0" time="1.013694">
	<testsuite tests="3" failures="2" time="0.510000" name="example/gosumtest" timestamp="2024-03-11T12:26:39+09:00">
		<properties>
			<property name="go.version" value="go1.22.1 darwin/arm64"></property>
		</properties>
		<testcase classname="example/gosumtest" name="TestMain/child_2" time="0.000000">
			<failure message="Failed" type="">=== RUN   TestMain/child_2&#xA;    main_test.go:12: error&#xA;--- FAIL: TestMain/child_2 (0.00s)&#xA;</failure>
		</testcase>
		<testcase classname="example/gosumtest" name="TestMain" time="0.000000">
			<failure message="Failed" type="">=== RUN   TestMain&#xA;--- FAIL: TestMain (0.00s)&#xA;</failure>
		</testcase>
		<testcase classname="example/gosumtest" name="TestMain/child_1" time="0.000000"></testcase>
	</testsuite>
</testsuites>

This output shows that nested test cases are squashed into the <testcase> layer by treating them as the same layer as their parent, TestMain. We can still distinguish nested ones by their name attributes that look like TestMain/<subtest_name>.

As described in #22795, vitest solves the issue in the same way as gotestsum.

One downside of this would be that one test failure that happens in a nested test case will end up being counted multiple times, because not only the subtest but also its wrapping container(s) are considered to be failures. In fact, in the gotestsum output above, TestMain/child_2 failed (which is totally expected) while its parent, TestMain, was also counted as failure. As #20273 (comment) pointed out, there is a test runner that offers flexibility to prevent this, but I personally don't think the "duplicate failure count" issue is a big deal.

How to fix the issue in this patch

This patch fixes the issue with the same approach as gotestsum and vitest.
More specifically, nested test cases are put into the <testcase> level and their names are now represented as squashed test names concatenated by > (e.g. parent 2 > child 1 > grandchild 1). This change also allows us to put a detailed error message as <failure> tag within the <testcase> tag, which should be handled nicely by third-party tools supporting JUnit XML.

Extra fix

Also, file paths embedded into XML outputs are changed from absolute path to relative path, which is helpful when running the test suites in several different environments like CI.

Resolves #22795

@@ -268,43 +268,77 @@ pub enum TestFailure {
HasSanitizersAndOverlaps(IndexSet<String>), // Long names of overlapped tests
}

impl ToString for TestFailure {
fn to_string(&self) -> String {
impl std::fmt::Display for TestFailure {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reviewers: this is just a refactor; ToString trait is automatically implemented for all types that implement std::fmt::Display, so basically it's recommended to implement std::fmt::Display

@magurotuna magurotuna force-pushed the improve-junit-reporter-for-steps branch from 683614a to 145f2a1 Compare March 11, 2024 07:06
@bartlomieju
Copy link
Member

One downside of this would be that one test failure that happens in a nested test case will end up being counted multiple times, because not only the subtest but also its wrapping container(s) are considered to be failures. In fact, in the gotestsum output above, TestMain/child_2 failed (which is totally expected) while its parent, TestMain, was also counted as failure. As #20273 (comment) pointed out, there is a test runner that offers flexibility to prevent this, but I personally don't think the "duplicate failure count" issue is a big deal.

I agree, this is a reasonable trade-off.

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great improvement. I have a couple of requests, but overall LGTM.

Pinging @littledivy for a second pair of eyes and to ensure npm_smoke_tests repo won't suddenly break because of this change.

Self {
path,
cwd: Url::from_directory_path(std::env::current_dir().unwrap()).unwrap(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will eventually fail and panic for user; can you pass down cwd from CliOptions so it's handled gracefully?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I brought this from the existing code of PrettyReporter:

cwd: Url::from_directory_path(std::env::current_dir().unwrap()).unwrap(),

Should we also fix these ones as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think it would be better to fix both then

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I noticed that all the test reporters have this code, so I'll fix all of them

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, it's done 752db03


impl TestNameTree {
fn new() -> Self {
Self(IndexMap::new())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe pre-allocate to something like 256 to avoid excessive reallocations?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! fixed in 14d89d2

@magurotuna magurotuna force-pushed the improve-junit-reporter-for-steps branch 2 times, most recently from 4d5ae5a to 752db03 Compare March 13, 2024 17:10
@bartlomieju bartlomieju added this to the 1.42 milestone Mar 20, 2024
@magurotuna magurotuna merged commit 64e8c36 into denoland:main Mar 25, 2024
17 checks passed
@magurotuna magurotuna deleted the improve-junit-reporter-for-steps branch March 25, 2024 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JUnit reporter: Output XML doesn't contain any details regarding failures on test "step"s
2 participants