-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Introduce support for before/after callbacks once per entire test run #19
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
Conversation
@ttddyy, this sounds more like an extension point rather than part of the standard programming model. In other words, if we support global callbacks like this I would envision them being implemented as a For example, we could consider introducing an What are your thoughts? Cheers, Sam |
+1 for such a feature (no opinion yet about the TestExecutionContext) |
I think this makes sense since global callbacks will probably not have direct interaction with specific test instances.
Yes, I like the idea using |
A concrete use case for such "global" listeners: frameworks like Arquillian or Pax Exam that start some sort of test container or server, run any number of test classes within that container and finally shut down the container. By "global", I mean "for all test classes", not only per engine. E.g. take a mixed collection of JUnit 4 and JUnit 5 test classes - it would not really make sense to start and stop the test server per engine, you'd want to start and stop it globally. I understand that The only open issue is how to register the The problem is, when running tests from Eclipse or Maven or any other IDE or build tool, the So I suggest the |
I think registering a |
Current coverage is
|
Hi, I wrote POC(or a pull request?) for per engine extension point as well as global extension point using I have mostly focused on how to populate So, it doesn't do anything with some of the items need to think about:
Thanks, |
I might be missing the right context but a global/per engine callback should IMO be outside the JUnit 5 API so ExtensionContext wouldn't be touched at all. Instead it would live in the launcher or engine module.
|
@jlink, there are numerous use cases where a global/per engine callback would need to provide something to tests or extensions (e.g., server name, port number, JDBC connection URL, a physical This is what @ttddyy is talking about with regard to the store in the |
@sbrannen Thanks for the explanation. Yes, that was my intention. For global callback, I used |
65beb90
to
e9cc300
Compare
Sorry for my premature comment earlier. I had a look at the code now and think I get the basic point. It's a big change in so far that it introduces a new - and IMO very strong - coupling, namly pushing information, uncontrolled by JUnit, from the outside to an inside test extension. This has a couple of drawbacks:
I think we'll have to discuss this suggestion thoroughly to see if the advantages outweigh the drawbacks. |
Aside from how to implement, I believe there are two requirements for this:
I think these two points can be combined together or can be separated. Another approach I can think of is having interfaces like this: // extension point for global before/after
public interface GlobalCallback {
void beforeExecute(...);
void afterExecute(...);
}
public interface ExtensionContextSourceProvider {
// return maybe Map<String, Object> but additionally needs to give id/name of it
ExtensionContext createTestContext();
}
// this class needs to be registered or discovered in discovery phase
public class MyTestContextProvider implements GlobalCallback, ExtensionContextSourceProvider {
@Override
public void beforeExecute(...) {
}
@Override
public TestContext createTestContext() {
// create context & populate data for ExtensionContext#store
// the context should have id/name.
// ex: "my-context" specified in MyExtensionPoint below
return ...;
}
@Override
public void afterExecute(...) {
}
}
@ParentExtensionContext("my-context") // specify parent context
public class MyExtensionPoint implements BeforeAllExtensionPoint, AfterAllExtensionPoint {
@Override
void beforeAll(ContainerExtensionContext context) throws Exception {
// the given context should have parent context set
};
....
}
All classes that implements
I believe this is certainly a good point to discuss. |
Closed in favor of #671. |
@marcphilipp Is any way in JUnit 5 now to Run some code once per test run? Or you completely deny this ability? |
@RomanIovlev Yes, there is a way to do that: you can write an extension like the following and register it globally. public class OncePerTestRunExtension implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
context.getRoot().getStore(ExtensionContext.Namespace.GLOBAL)
.getOrComputeIfAbsent(MyFixture.class);
}
static class MyFixture implements ExtensionContext.Store.CloseableResource {
public MyFixture() {
// setup
}
@Override
public void close() {
// teardown
}
}
} |
Thanks, but Why you don't want to implement this as Annotation? |
How would we discover classes with such annotations? It sounds like we'd have to scan the classpath even if only a single class was requested. |
hi All! Above advises do not work for me, so I solved this problem like this: Add to your Base abstract class (I mean abstract class where you initialize your driver in setUpDriver() method) this part of code: private static boolean started = false;
static{
if (!started) {
started = true;
try {
setUpDriver(); //method where you initialize your driver
} catch (MalformedURLException e) {
}
}
} And now, if your test classes will extends from Base abstract class -> setUpDriver() method will be executed before first @test only ONE time per project run. |
I have written an extension. Just to be sure it isn't a pom.xml issue. How would you add the junit.jupiter.extensions.autodetection.enabled property=true to the maven-surefire-plugin? This is the XML I have so far in pom.xml <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<systemPropertyVariables>
<junit.jupiter.extensions.autodetection.enabled>true</junit.jupiter.extensions.autodetection.enabled>
</systemPropertyVariables>
</configuration>
</plugin> No errors reported. The call back before each test is not executed. |
@jbwyatt4 That looks like it should work. Could you debug or print all system properties in your test to see if Surefire hands them to the forked JVM? |
The XML property reports being true.
Posting code for extension class: @ExtendWith({YourJUnitExtension.class})
public class YourJUnitExtension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
private static boolean started = false;
@Override
public void beforeAll(ExtensionContext context) {
if (!started) {
started = true;
CU.enable(); // custom class to control which print statements print
CU.print("Print from Extension Context");
CU.disable();
}
}
@Override
public void close() {
// Your "after all tests" logic goes here
}
} |
If so, the However, for This should work and eventually be documented in #1555: public class YourJUnitExtension implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) {
ExtensionContext.Store rootStore = context.getRoot().getStore(ExtensionContext.Namespace.GLOBAL);
rootStore.getOrComputeIfAbsent("myResource", key -> {
CU.enable(); // custom class to control which print statements print
CU.print("Print from Extension Context");
CU.disable();
return new MyResource();
});
}
static class MyResource implements ExtensionContext.Store.CloseableResource {
@Override
public void close() throws Throwable {
// Your "after all tests" logic goes here
}
}
} |
Just got the notification from GitHub on this. Your code works. I believe you were right about @ExtendWith. Thank you for your help @marcphilipp. |
Initially wrote it here.
Nice to have callback interfaces once per entire test run or engine/tag/etc. (e.g.: @BeforeAllTests/@AfterAllTests)
Would be nice that they receive
TestExecutionContext
in argument, so that implementations can populate beans which could be resolved/used/injected across all tests.