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

Document scope of applicability for TestWatcher implementations #3234

Closed
wazzeps opened this issue Apr 5, 2023 · 11 comments
Closed

Document scope of applicability for TestWatcher implementations #3234

wazzeps opened this issue Apr 5, 2023 · 11 comments

Comments

@wazzeps
Copy link

wazzeps commented Apr 5, 2023

Referring to #2560

Steps to reproduce

	@Disabled
	@ParameterizedTest
	@ValueSource(strings = {"a", "b"})
	void testParam(String s) {
		System.out.println(s);
	}

With TestWatcher implementation

	@Override
	public void testDisabled(ExtensionContext context, Optional<String> reason) {
		System.out.println("DISABLED");
	}

Expected: 2 outputs "DISABLED"
Actual: no output, testDisabled method wasn't called

Context

  • junit-jupiter-api:5.9.2
  • junit-jupiter-engine:5.9.2
  • Gradle
  • IntelliJ IDEA
@sbrannen sbrannen changed the title Tests with @Disabled and @ParameterizedTest are not running @Disabled events for test templates are not supplied to TestWatcher Apr 6, 2023
@sbrannen
Copy link
Member

sbrannen commented Apr 6, 2023

I have confirmed that disabled events are not supplied to a TestWatcher for @TestTemplate methods (e.g., @RepeatedTest and @ParameterizedTest, and I have changed the title of this issue to reflect that.

The following can be used to reproduce the issue.

class WatcherTests {

	@RegisterExtension
	Extension watcher = new TestWatcher() {

		@Override
		public void testSuccessful(ExtensionContext context) {
			System.err.println("SUCCESSFUL: " + context.getRequiredTestMethod().getName());
		}

		@Override
		public void testDisabled(ExtensionContext context, Optional<String> reason) {
			System.err.println("DISABLED  : " + context.getRequiredTestMethod().getName());
		}
	};

	@Disabled
	@Test
	void test() {
	}

	@Disabled
	@RepeatedTest(2)
	void repeatedTest() {
	}

	@Disabled
	@ParameterizedTest
	@ValueSource(strings = { "a", "b" })
	void parameterizedTest(String s) {
	}

}

Since the documentation states the following, I am labeling this as a bug.

Please note that this API is currently only used to report the results of @test methods and @TestTemplate methods (e.g., @RepeatedTest and @ParameterizedTest).

Thanks for raising the issue, @wazzeps.

@sbrannen sbrannen changed the title @Disabled events for test templates are not supplied to TestWatcher @Disabled events for @TestTemplate methods are not supplied to TestWatcher Apr 6, 2023
@sbrannen sbrannen added this to the 5.10.0-M1 milestone Apr 6, 2023
@sbrannen
Copy link
Member

sbrannen commented Apr 6, 2023

Oops. I retract that.

Everything works fine if the TestWatcher is registered at the class level.

For example, changing the declaration to static as follows causes the disabled events to be reported as expected.

	@RegisterExtension
	static Extension watcher = new TestWatcher() { // ...

I am therefore repurposing this issue to improve the documentation for TestWatcher to make users of the API aware of the different semantics when a TestWatcher is not registered at the class level.

@sbrannen sbrannen changed the title @Disabled events for @TestTemplate methods are not supplied to TestWatcher Document scope issues for TestWatcher implementations Apr 6, 2023
@wazzeps
Copy link
Author

wazzeps commented Apr 6, 2023

If I understand correctly, using an extension with @ExtendWith in a test class declaration is also registering at the class level.

@ExtendWith(TestWatcherImpl.class)
public class SomeTest {

In this case TestWatcher methods are not called.

@sbrannen
Copy link
Member

sbrannen commented Apr 6, 2023

In this case TestWatcher methods are not called.

I am not able to reproduce that.

When I execute the following, it prints out:

DISABLED  : repeatedTest
DISABLED  : test
DISABLED  : parameterizedTest
@ExtendWith(WatcherTests.MyWatcher.class)
class WatcherTests {

	static class MyWatcher implements TestWatcher{
		@Override
		public void testSuccessful(ExtensionContext context) {
			System.err.println("SUCCESSFUL: " + context.getRequiredTestMethod().getName());
		}

		@Override
		public void testDisabled(ExtensionContext context, Optional<String> reason) {
			System.err.println("DISABLED  : " + context.getRequiredTestMethod().getName());
		}
	}

	@Disabled
	@Test
	void test() {
	}

	@Disabled
	@RepeatedTest(2)
	void repeatedTest() {
	}

	@Disabled
	@ParameterizedTest
	@ValueSource(strings = { "a", "b" })
	void parameterizedTest(String s) {
	}

}

@wazzeps
Copy link
Author

wazzeps commented Apr 6, 2023

In my case, I implemented TestWatcher in a separate class

public class TestWatcherImpl implements TestWatcher {

and then

@ExtendWith(TestWatcherImpl.class)
public class SomeTest {

@sbrannen
Copy link
Member

sbrannen commented Apr 7, 2023

In my case, I implemented TestWatcher in a separate class

That doesn't make a difference. A top-level class exhibits the same behavior as a static nested class.

For example, the following also prints:

DISABLED  : repeatedTest
DISABLED  : test
DISABLED  : parameterizedTest
@ExtendWith(MyWatcher.class)
public class WatcherTests {

	@Disabled
	@Test
	void test() {
	}

	@Disabled
	@RepeatedTest(2)
	void repeatedTest() {
	}

	@Disabled
	@ParameterizedTest
	@ValueSource(strings = { "a", "b" })
	void parameterizedTest(String s) {
	}

}

class MyWatcher implements TestWatcher {
	@Override
	public void testSuccessful(ExtensionContext context) {
		System.err.println("SUCCESSFUL: " + context.getRequiredTestMethod().getName());
	}

	@Override
	public void testDisabled(ExtensionContext context, Optional<String> reason) {
		System.err.println("DISABLED  : " + context.getRequiredTestMethod().getName());
	}
}

@wazzeps
Copy link
Author

wazzeps commented Apr 7, 2023

DISABLED : repeatedTest
DISABLED : test
DISABLED : parameterizedTest

Both of your examples end with DISABLED : parameterizedTest.

Could you please check what happens if the test with @ParameterizedTest runs first?

@sbrannen
Copy link
Member

sbrannen commented Apr 7, 2023

It doesn't make a difference if the parameterized test runs first.

@ExtendWith(WatcherTests.MyWatcher.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class WatcherTests {

	static class MyWatcher implements TestWatcher {

		@Override
		public void testSuccessful(ExtensionContext context) {
			System.err.println("SUCCESSFUL: " + context.getRequiredTestMethod().getName());
		}

		@Override
		public void testDisabled(ExtensionContext context, Optional<String> reason) {
			System.err.println("DISABLED  : " + context.getRequiredTestMethod().getName());
		}
	}


	@Order(1)
	@Disabled
	@ParameterizedTest
	@ValueSource(strings = { "a", "b" })
	void parameterizedTest(String s) {
	}

	@Order(2)
	@Disabled
	@Test
	void test() {
	}

	@Order(3)
	@Disabled
	@RepeatedTest(2)
	void repeatedTest() {
	}

}

Here's the result:

DISABLED  : parameterizedTest
DISABLED  : test
DISABLED  : repeatedTest

@wazzeps
Copy link
Author

wazzeps commented Apr 7, 2023

Interesting. I got different results.

@Order(1)
@Disabled
@ParameterizedTest
@ValueSource(strings = { "a", "b" })
void parameterizedTest(String s) {
}

@Order(2)
@Disabled
@Test
void test() {
}

@Order(3)
@Disabled
@RepeatedTest(2)
void repeatedTest() {
}

then

DISABLED  : parameterizedTest
DISABLED  : repeatedTest
@Order(1)
@Disabled
@ParameterizedTest
@ValueSource(strings = { "a", "b" })
void parameterizedTest(String s) {
}

@Order(2)
@Disabled
@RepeatedTest(2)
void repeatedTest() {
}

@Order(3)
@Disabled
@Test
void test() {
}

then

DISABLED : parameterizedTest

@marcphilipp marcphilipp modified the milestones: 5.10.0-M1, 5.10.0-RC1 Apr 21, 2023
@Jusjuc
Copy link

Jusjuc commented May 19, 2023

If you annotate a test class with @Disabled, the watcher doesn't get invoked for any type of test.

@Disabled
class WatcherTests {

    @RegisterExtension
    static Extension watcher = new TestWatcher() {
        @Override
        public void testSuccessful(ExtensionContext context) {
            System.err.println("SUCCESSFUL: " + context.getRequiredTestMethod().getName());
        }

        @Override
        public void testDisabled(ExtensionContext context, Optional<String> reason) {
            System.err.println("DISABLED  : " + context.getRequiredTestMethod().getName());
        }
    };

    @Test
    void test() {
    }

    @RepeatedTest(2)
    void repeatedTest() {
    }

    @ParameterizedTest
    @ValueSource(strings = { "a", "b" })
    void parameterizedTest(String s) {
    }
}

The output is empty in such case.

@sbrannen sbrannen self-assigned this May 30, 2023
@sbrannen
Copy link
Member

If you annotate a test class with @Disabled, the watcher doesn't get invoked for any type of test.

Good point. 👍

The documentation already states the following.

if there is a failure at the class level — for example, an exception thrown by a @BeforeAll method — no test results will be reported.

So that's similar in nature to the @Disabled test class scenario, and I'll update the documentation to reflect that.

@sbrannen sbrannen changed the title Document scope issues for TestWatcher implementations Document scope of applicability for TestWatcher implementations Jun 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants