-
-
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 adding 'shared resources' programmatically #3889
base: main
Are you sure you want to change the base?
Changes from 12 commits
7fc2b96
cde4bc8
00788b6
ff3c786
1c6eef8
7d894c5
1f8a6d0
e1b5473
4460685
e14bfda
ba476e5
1d8488c
3fddd55
49f9a28
d56196d
c8d26ef
9383c39
249ab3e
67e7dc5
792b43a
ef2f38f
730a99d
cb01e24
35acb01
3d8af62
780c03b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2927,21 +2927,30 @@ In addition to controlling the execution mode using the `{Execution}` annotation | |
Jupiter provides another annotation-based declarative synchronization mechanism. The | ||
`{ResourceLock}` annotation allows you to declare that a test class or method uses a | ||
specific shared resource that requires synchronized access to ensure reliable test | ||
execution. The shared resource is identified by a unique name which is a `String`. The | ||
name can be user-defined or one of the predefined constants in `{Resources}`: | ||
execution. The shared resource is identified by a unique name which is a `String`. | ||
The name can be user-defined or one of the predefined constants in `{Resources}`: | ||
`SYSTEM_PROPERTIES`, `SYSTEM_OUT`, `SYSTEM_ERR`, `LOCALE`, or `TIME_ZONE`. | ||
|
||
If the tests in the following example were run in parallel _without_ the use of | ||
{ResourceLock}, they would be _flaky_. Sometimes they would pass, and at other times they | ||
would fail due to the inherent race condition of writing and then reading the same JVM | ||
System Property. | ||
Since Junit Jupiter 5.12 `{ResourceLock}` annotation has 'providers' attribute | ||
which accepts an array of one or more classes implementing `{ResourceLocksProvider}` | ||
interface. This interface allows to add shared resources in runtime. | ||
|
||
When access to shared resources is declared using the `{ResourceLock}` annotation, the | ||
JUnit Jupiter engine uses this information to ensure that no conflicting tests are run in | ||
parallel. This guarantee extends to lifecycle methods of a test class or method. For | ||
example, if a test method is annotated with a `{ResourceLock}` annotation, the "lock" will | ||
be acquired before any `@BeforeEach` methods are executed and released after all | ||
`@AfterEach` methods have been executed. | ||
Note that resources declared "statically" with `{ResourceLock}` annotation | ||
are combined with resources added "dynamically" with `{ResourceLocksProvider}` | ||
interface. | ||
|
||
If the tests in the following examples were run in parallel _without_ the use of | ||
`{ResourceLock}` or `{ResourceLocksProvider}`, they would be _flaky_. | ||
Sometimes they would pass, and at other times they would fail due to | ||
the inherent race condition of writing and then reading the same JVM System Property. | ||
|
||
When access to shared resources is declared using the `{ResourceLock}` annotation | ||
or added with `{ResourceLocksProvider}` interface, the JUnit Jupiter engine uses | ||
this information to ensure that no conflicting tests are run in parallel. | ||
This guarantee extends to lifecycle methods of a test class or method. | ||
For example, if a test method is annotated with a `{ResourceLock}` annotation, | ||
the "lock" will be acquired before any `@BeforeEach` methods are executed | ||
and released after all `@AfterEach` methods have been executed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please revert the changes made to the formatting of these two paragraphs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
[NOTE] | ||
.Running tests in isolation | ||
|
@@ -2958,8 +2967,15 @@ parallel with each other but not while any other test that requires `READ_WRITE` | |
to the same shared resource is running. | ||
|
||
[source,java] | ||
.Declaring shared resources "statically" with `{ResourceLock}` annotation | ||
---- | ||
include::{testDir}/example/sharedresources/StaticSharedResourcesDemo.java[tags=user_guide] | ||
---- | ||
|
||
[source,java] | ||
.Adding shared resources "dynamically" with `{ResourceLocksProvider}` interface | ||
---- | ||
include::{testDir}/example/SharedResourcesDemo.java[tags=user_guide] | ||
include::{testDir}/example/sharedresources/DynamicSharedResourcesDemo.java[tags=user_guide] | ||
---- | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* Copyright 2015-2024 the original author or authors. | ||
* | ||
* All rights reserved. This program and the accompanying materials are | ||
* made available under the terms of the Eclipse Public License v2.0 which | ||
* accompanies this distribution and is available at | ||
* | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
|
||
package example.sharedresources; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNull; | ||
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; | ||
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ; | ||
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE; | ||
import static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.HashSet; | ||
import java.util.Properties; | ||
import java.util.Set; | ||
|
||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.parallel.Execution; | ||
import org.junit.jupiter.api.parallel.ResourceAccessMode; | ||
import org.junit.jupiter.api.parallel.ResourceLock; | ||
import org.junit.jupiter.api.parallel.ResourceLocksProvider; | ||
|
||
// tag::user_guide[] | ||
@Execution(CONCURRENT) | ||
@ResourceLock(providers = DynamicSharedResourcesDemo.Provider.class) | ||
class DynamicSharedResourcesDemo { | ||
|
||
private Properties backup; | ||
|
||
@BeforeEach | ||
void backup() { | ||
backup = new Properties(); | ||
backup.putAll(System.getProperties()); | ||
} | ||
|
||
@AfterEach | ||
void restore() { | ||
System.setProperties(backup); | ||
} | ||
|
||
@Test | ||
void customPropertyIsNotSetByDefault() { | ||
assertNull(System.getProperty("my.prop")); | ||
} | ||
|
||
@Test | ||
void canSetCustomPropertyToApple() { | ||
System.setProperty("my.prop", "apple"); | ||
assertEquals("apple", System.getProperty("my.prop")); | ||
} | ||
|
||
@Test | ||
void canSetCustomPropertyToBanana() { | ||
System.setProperty("my.prop", "banana"); | ||
assertEquals("banana", System.getProperty("my.prop")); | ||
} | ||
|
||
static final class Provider implements ResourceLocksProvider { | ||
|
||
@Override | ||
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) { | ||
ResourceAccessMode mode; | ||
if (testMethod.getName().equals("customPropertyIsNotSetByDefault")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would make for a better example if we'd check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. |
||
mode = READ; | ||
} | ||
else { | ||
mode = READ_WRITE; | ||
} | ||
Set<Lock> locks = new HashSet<>(); | ||
locks.add(new Lock(SYSTEM_PROPERTIES, mode)); | ||
return locks; | ||
} | ||
} | ||
|
||
} | ||
// end::user_guide[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the User Guide is versioned, we usually avoid mentioning specific versions when features were introduced. Thus, please remove that bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Done