Skip to content

Commit

Permalink
[JENKINS-48466] Provide JUnit 5 support for JenkinsRule (#438)
Browse files Browse the repository at this point in the history
Co-authored-by: offa <[email protected]>
Co-authored-by: Réda Housni Alaoui <[email protected]>
  • Loading branch information
3 people authored Jul 18, 2022
1 parent 6d4e97d commit 14f5dba
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 0 deletions.
21 changes: 21 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ THE SOFTWARE.
<artifactId>annotation-indexer</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.8.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-annotation</artifactId>
Expand Down Expand Up @@ -163,6 +170,20 @@ THE SOFTWARE.
<artifactId>hamcrest-library</artifactId>
<version>${hamcrest.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>embedded-rhino-debugger</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.jvnet.hudson.test.junit.jupiter;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.lang.reflect.Method;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.runner.Description;
import org.jvnet.hudson.test.JenkinsRecipe;
import org.jvnet.hudson.test.JenkinsRule;

/**
* Provides JUnit 5 compatibility for {@link JenkinsRule}.
*/
class JUnit5JenkinsRule extends JenkinsRule {
private final ParameterContext context;

JUnit5JenkinsRule(@NonNull ParameterContext context, @NonNull ExtensionContext extensionContext) {
this.context = context;
this.testDescription = Description.createTestDescription(
extensionContext.getTestClass().map(Class::getName).orElse(null),
extensionContext.getTestMethod().map(Method::getName).orElse(null));
}

@Override
public void recipe() throws Exception {
JenkinsRecipe jenkinsRecipe = context.findAnnotation(JenkinsRecipe.class).orElse(null);
if (jenkinsRecipe == null) {
return;
}
@SuppressWarnings("unchecked")
final JenkinsRecipe.Runner<JenkinsRecipe> runner =
(JenkinsRecipe.Runner<JenkinsRecipe>) jenkinsRecipe.value().getDeclaredConstructor().newInstance();
recipes.add(runner);
tearDowns.add(() -> runner.tearDown(this, jenkinsRecipe));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.jvnet.hudson.test.junit.jupiter;

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.jvnet.hudson.test.JenkinsRule;

/**
* JUnit 5 extension providing {@link JenkinsRule} integration.
*
* @see WithJenkins
*/
class JenkinsExtension implements ParameterResolver, AfterEachCallback {

private static final String KEY = "jenkins-instance";
private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(JenkinsExtension.class);

@Override
public void afterEach(ExtensionContext context) throws Exception {
final JenkinsRule rule = context.getStore(NAMESPACE).remove(KEY, JenkinsRule.class);
if (rule == null) {
return;
}
rule.after();
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType().equals(JenkinsRule.class);
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
final JenkinsRule rule =
extensionContext
.getStore(NAMESPACE)
.getOrComputeIfAbsent(
KEY,
key -> new JUnit5JenkinsRule(parameterContext, extensionContext),
JenkinsRule.class);

try {
rule.before();
return rule;
} catch (Throwable t) {
throw new ParameterResolutionException(t.getMessage(), t);
}
}
}
65 changes: 65 additions & 0 deletions src/main/java/org/jvnet/hudson/test/junit/jupiter/WithJenkins.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.jvnet.hudson.test.junit.jupiter;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
import org.jvnet.hudson.test.JenkinsRule;

/**
* JUnit 5 meta annotation providing {@link JenkinsRule JenkinsRule} integration.
*
* <p>Test methods using the rule extension need to accept it by {@link JenkinsRule JenkinsRule} parameter;
* each test case gets a new rule object.
* An annotated method without a {@link JenkinsRule JenkinsRule} parameter behaves as if it were not annotated.
*
* <p>Annotating a <em>class</em> provides access for all of its tests.
* Unrelated test cases can omit the parameter.
*
* <blockquote>
*
* <pre>
* &#64;WithJenkins
* class ExampleJUnit5Test {
*
* &#64;Test
* public void example(JenkinsRule r) {
* // use 'r' ...
* }
*
* &#64;Test
* public void exampleNotUsingRule() {
* // ...
* }
* }
* </pre>
*
* </blockquote>
*
* <p>Annotating a <i>method</i> limits access to the method.
*
* <blockquote>
*
* <pre>
* class ExampleJUnit5Test {
*
* &#64;WithJenkins
* &#64;Test
* public void example(JenkinsRule r) {
* // use 'r' ...
* }
* }
* </pre>
*
* </blockquote>
*
* @see JenkinsExtension
* @see org.junit.jupiter.api.extension.ExtendWith
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ExtendWith(JenkinsExtension.class)
public @interface WithJenkins {}
2 changes: 2 additions & 0 deletions src/spotbugs/excludesFilter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@
<Class name="org.jvnet.hudson.test.JenkinsRecipe$Runner"/>
<Class name="org.jvnet.hudson.test.JenkinsSessionRule$CustomJenkinsRule"/>
<Class name="org.jvnet.hudson.test.junit.GroupedTest"/>
<Class name="org.jvnet.hudson.test.junit.jupiter.JenkinsExtension"/>
<Class name="org.jvnet.hudson.test.junit.jupiter.JUnit5JenkinsRule"/>
<Class name="org.jvnet.hudson.test.LenientRunnable"/>
<Class name="org.jvnet.hudson.test.MemoryAssert"/>
<Class name="org.jvnet.hudson.test.NoListenerConfiguration"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jvnet.hudson.test.junit.jupiter;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsEmptyCollection.empty;

import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.JenkinsRule;

@WithJenkins
class JenkinsRuleResolverTest {

@Test
void jenkinsRuleIsAccessible(JenkinsRule rule) throws IOException {
assertThat(rule.jenkins.getJobNames(), empty());
rule.createFreeStyleProject("job-0");
assertThat(rule.jenkins.getJobNames(), hasSize(1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jvnet.hudson.test.junit.jupiter;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsEmptyCollection.empty;

import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.JenkinsRule;

class JenkinsRuleResolverWithMethodScopeTest {

@Test
@WithJenkins
void jenkinsRuleIsAccessible(JenkinsRule rule) throws IOException {
assertThat(rule.jenkins.getJobNames(), empty());
rule.createFreeStyleProject("job-0");
assertThat(rule.jenkins.getJobNames(), hasSize(1));
}
}

0 comments on commit 14f5dba

Please sign in to comment.