-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Introduce BeforeTestInstantiationCallback extension API #839
Comments
Thanks for opening the issue! There are some details I need clarifications on because I'm not quite getting what you describe. (Excuse me if these are dumb question. 😃)
What exactly do you mean by "note"? Since
What do you mean with "switch"? Do you create a new Another, more general question: Obviously the fields must always be initialized in the same order for the randomization to be reproducible. Does the JLS or JVMLS guarantee an order? Because if not, the entire premise is built on an unspecified implementation detail. |
Sorry, that it took me a while to answer!! Thanks for taking the time to look into this 😃 I'm not amazed, that I did not get across clearly, it's not the easiest thing to describe somehow 😉 By "note" I just meant to set and store the current seed somewhere for later (set and store static seed -> static init -> set and store instance seed -> instance init) The Yes, at the moment right before any instance related initialization first happens (field init, constructor, ...) we want to create a new As far as I understand http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5 the order should be deterministic, "in the left-to-right order in which they appear textually in the source code", so theoretically (and empirically so far 😉) this should work. |
Thanks for clearing this up, this is an interesting problem. 😃
Forgot to answer that... No, not at all. It's of course up to people with more knowledge of the internals (summoning @sormuras) to decide that but I see no conceptual reason why that would be a bad idea. Still, gnawing on your problem as it is... Did you ever consider injecting One caveat is that reflection order is not guaranteed, so injecting the same |
When you say that the " Please note as well that if a user annotates a test class with |
Yes, I was referring to a static initialization block, and I think JUnit 5 at the moment does sth. like public class FooBeforeAll implements BeforeAllCallback {
@Override
public void beforeAll(ContainerExtensionContext context) throws Exception {
System.out.println("BEFORE ALL");
}
} @ExtendWith(FooBeforeAll.class)
public class FooTest {
static {
System.out.println("STATIC");
}
@Test
void name() {
System.out.println("TESTS");
}
} will print
But anyway, I'm not convinced, that it is a good idea, to rely on that 😉 So, we would have to add some sort of lazy mechanism for this, too. I think if I would start from scratch, I would follow @nicolaiparlog 's approach, and kick all this evil static factory code (even though it causes a little more boiler plate to set all those fields, but in my opinion we could just forbid static random fields in tests anyway). However, the other question is, if it is not a valid request in general, to have an extension point right before a test class is instantiated. |
I agree: that's fragile at best. In your example it's obviously true that static initialization blocks of the test class are not invoked prior to invocation of However, the following (a bit contrived) example demonstrates that that will not always be the case. @ExtendWith(FooBeforeAll.class)
class FooTest {
static final String TEST_CLASS_NAME = FooTest.class.getName();
static {
System.out.println("STATIC");
}
@org.junit.jupiter.api.Test
void name() {
System.out.println("TESTS");
}
}
class FooBeforeAll implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
System.out.println("BEFORE ALL: " + FooTest.TEST_CLASS_NAME);
}
} If any other class actively uses the test class earlier, the output to the console will be the following.
So, in summary, I would not implement an extension that hopes that the test class has not yet been fully initialized, because as a wise person once said, "Hope is not a strategy!". 😉 |
Okay, I guess this concrete example makes it even more clear, that relying on the order of static vs beforeAll is a bad idea 😉 |
We're still pondering that. In other words, we're basically waiting to see if the community at large really needs such a feature (i.e., whether it warrants inclusion in the extension API). |
Hi again, Good job :-), definitely a much more powerful and general enhancement than our original proposal. One thing that I wasn't aware of in that context is the fact that the test class may not have been initialized yet (clinit), when the interceptTestClassConstructor()-method is invoked. It may be worth mentioning that in the javadoc of the method. |
@jonashoef, can you please create a new issue (or even a PR) for that? Otherwise comments like these get lost the backlog. Thanks |
Provided PR |
This issue has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be automatically closed if no further activity occurs. Thank you for your contribution. |
This issue has been automatically closed due to inactivity. If you have a good use case for this feature, please feel free to reopen the issue. |
Short story:
For our test infrastructure to create test data, we must distinguish calls during the static initialization of each test class from calls during test instantiation for each test method.
This could be solved by the proposed
BeforeTestInstantiationCallback
.Long story:
When we write our tests, we want to restrict the data that have to be set explicitly to those that the specific test requires. All other properties are set randomly, but valid. Naturally using random data may introduce randomly failing tests, so we need the means to easily reproduce test failures that e.g. happen in the CI environment.
Reproducing such failures requires knowing the seed values for our random data when the specific test was executed. This is where we want to take advantage of a JUnit 5 extension. It has to store one seed value at the class level, for example if static constants are initialized using our random mechanism. In addition, it has to store the seed value at the instance level, for each test run. If a test should fail, the respective seed values must be reported in the failure message. If the failure is related to the use of random data, all this together allows us to reproduce it in any other environment. Example:
We note the class seed in a
BeforeAllCallback
, which is the perfect place. The switch to using the instance seed however, has to be right before the test instance is created, to cover random data used for instance field initialization, which is a rather common use case in our tests.Obviously, the
TestInstancePostProcessor
callback is too late for switching from the class seed to the instance seed. Currently we have an ugly workaround, where we check the stacktrace to switch to the instance seed, as soon as we detect a call from the test's constructor. If there are no such calls we switch to the instance seed in theTestInstancePostProcessor
callback.The proposed
BeforeTestInstantiationCallback
would be the natural place to make the required switch to the instance seed trivial.Do you think, adding the
BeforeTestInstantiationCallback
would be harmful?The text was updated successfully, but these errors were encountered: