-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Detect annotations on overridden super type methods #960
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
FYI: I have updated the title and description. |
Works in JUnit 4import org.junit.Before;
import org.junit.Test;
public abstract class AbstractTests {
@Before
public abstract void before();
@Test
public abstract void test();
} public class FooTests extends AbstractTests {
@Override
// @Before
public void before() {
System.err.println("before");
}
@Override
// @Test
public void test() {
System.err.println("test");
}
} |
Does not work in JUnit Jupiter 5.0 M5import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public abstract class AbstractTests {
@BeforeEach
public abstract void before();
@Test
public abstract void test();
} public class FooTests extends AbstractTests {
@Override
// @BeforeEach
public void before() {
System.err.println("before");
}
@Override
// @Test
public void test() {
System.err.println("test");
}
} |
Does not work in JUnit Jupiter 5.0 M5import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
interface InterfaceTests {
@BeforeEach
void before();
@Test
void test();
} public class BarTests implements InterfaceTests {
@Override
// @BeforeEach
public void before() {
System.err.println("before");
}
@Override
// @Test
public void test() {
System.err.println("test");
}
} |
My preliminary analysis of this functionality in JUnit 4 leads me to believe that the behavior is perhaps accidental, especially in light of explicit documentation within JUnit 4's /**
* Returns the methods that run tests. Default implementation returns all
* methods annotated with {@code @Test} on this class and superclasses that
* are not overridden.
*/
protected List<FrameworkMethod> computeTestMethods() {
return getTestClass().getAnnotatedMethods(Test.class);
} A round of debugging led me to the fact that the following is actually what JUnit 4 tracks as the "test method" in such cases.
In summary: it appears that JUnit 4 in fact does not have any special reflective code in place to support "implicit inheritance" of annotations of methods. Rather, it only works coincidentally since JUnit 4 erroneously (?) tracks annotated abstract methods. If a concrete method (that overrides an annotated abstract method) is not annotated, it simply gets ignored since it is in fact "not a test method". Then the execution of the abstract method just happens to work by chance (?) since the methods have the same name. In light of these findings, I am removing the "bug" label, adding the "team discussion" label, and moving this issue to 5.0 RC1. |
Added Platform label since such a change will (adversely?) affect any lookups for annotations on methods across all use cases. |
We also need to consider cases where the subclass adds an annotation, e.g. public class BaseTests {
@Test
public void test() {/*...*/}
} public class SubTests extends BaseTests {
@Override
@Disabled
public void test() {/*...*/}
} This would be equivalent to: public class SubTests {
@Test
@Disabled
public void test() {/*...*/}
} This becomes even more interesting, if the subclass declares annotations that the base class declares as well. public class BaseTests {
@Test
@DisplayName("base")
@Tag("base")
public void test() {/*...*/}
} public class SubTests extends BaseTests {
@Override
@DisplayName("sub")
@Tag("sub")
public void test() {/*...*/}
} In this case the display name should be "sub" but it should/could have both tags, so it would be equivalent to: public class SubTests {
@Test
@DisplayName("sub")
@Tag("base")
@Tag("sub")
public void test() {/*...*/}
} To override a repeatable annotation completely, you could use its container annotation: public class SubTests extends BaseTests {
@Override
@DisplayName("sub")
@Tags({@Tag("sub")})
public void test() {/*...*/}
} which would then yield only the tag "sub". |
Yep, that opens up a whole new can of worms. Not so sure that I'm a fan of this approach unless we keep it super simple. |
Added additional links to the Related Discussions section thanks to a few tips from @marcphilipp. |
@marcphilipp are there any plans to implement it? |
@remal No, it's currently not planned to be implemented. What's your concrete use case? |
@marcphilipp the use case is quite simple: I have several implementations of an interface and I'd like to use a base abstract test class (with abstract methods annotated with |
Imo silently skipping overridden annotated method is the worst solution. It should be either checked for such case and rejected with clear error message or supported as in junit4 (the latter is my personal preference: |
Team decision: Keep the current behavior to avoid a breaking change and considerable added complexity. |
Is this behavior documented somewhere (and should it be)? |
Yes, it's documented on all method-level annotations, e.g.
|
Closely related to: |
Turns out `@Test` annotations have no effect when set on methods in an `abstract` class. junit-team/junit5#960 Signed-off-by: nscuro <nscuro@protonmail.com>
Turns out `@Test` annotations have no effect when set on methods in an `abstract` class. junit-team/junit5#960 Signed-off-by: nscuro <nscuro@protonmail.com>
Turns out `@Test` annotations have no effect when set on methods in an `abstract` class. junit-team/junit5#960 Signed-off-by: nscuro <nscuro@protonmail.com>
Overview
In JUnit 4, if
@Test
is declared on anabstract
method in a superclass, then any concrete implementation of the method is not required to redeclare the@Test
annotation. However, JUnit Jupiter currently does not support implicitly inherited annotations on overridden methods.This is an unintentional difference between JUnit 4 and JUnit Jupiter.
Related Discussions
Deliverables
The text was updated successfully, but these errors were encountered: