Skip to content

Commit

Permalink
Introduce support for injecting the current invocation index
Browse files Browse the repository at this point in the history
This commit introduces a new @InvocationIndex annotation for injecting
the current invocation index into @test, @beforeeach, and @AfterEach
methods.

Furthermore, support has been added for injecting the repetition count
for @RepeatedTest methods using @InvocationIndex.

Issues: #14, #214
  • Loading branch information
sbrannen committed Mar 19, 2017
1 parent bf008e8 commit d2e3a4d
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2015-2017 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 v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.jupiter.api;

import static org.junit.platform.commons.meta.API.Usage.Experimental;

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.platform.commons.meta.API;

/**
* {@code @InvocationIndex} is used to inject the current <em>invocation index</em>
* into {@code @Test}, {@code @BeforeEach}, and {@code @AfterEach} methods.
*
* <p>An invocation index is a counter associated with a given
* {@link org.junit.jupiter.api.extension.TestTemplateInvocationContext
* TestTemplateInvocationContext}, such as the current repetition in a
* {@link RepeatedTest @RepeatedTest}.
*
* <p>If a method parameter is of type {@code int} or {@code Integer} and
* annotated with {@code @InvocationIndex}, JUnit will supply the current
* <em>invocation index</em> as the value for the parameter.
*
* @since 5.0
* @see RepeatedTest
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(Experimental)
public @interface InvocationIndex {
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
*
* <p>Each invocation of the repeated test behaves like the execution of a
* regular {@link Test @Test} method will full support for the same lifecycle
* callbacks and extensions.
* callbacks and extensions. In addition, the current <em>invocation index</em>
* (i.e., repetition count) can be injected via {@link InvocationIndex @InvocationIndex}
*
* <p>{@code @RepeatedTest} methods must not be {@code private} or {@code static}
* and must return {@code void}.
Expand All @@ -43,6 +44,7 @@
*
* @since 5.0
* @see DisplayName
* @see InvocationIndex
* @see TestInfo
* @see TestTemplate
* @see Test
Expand All @@ -68,7 +70,8 @@
* <ul>
* <li><code>{displayName}</code>: the {@linkplain TestInfo#getDisplayName
* display name} of the {@code @RepeatedTest} method</li>
* <li><code>{index}</code>: the current invocation index (i.e., repetition count)</li>
* <li><code>{index}</code>: the current <em>invocation index</em> (i.e.,
* repetition count)</li>
* </ul>
*
* <p>Defaults to <code>"{displayName} :: repetition {index}"</code>.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2015-2017 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 v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.jupiter.engine.extension;

import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;

import org.junit.jupiter.api.InvocationIndex;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolver;

/**
* {@link ParameterResolver} that supports resolution of the current
* <em>invocation index</em> for parameters annotated with {@code @InvocationIndex}.
*
* @since 5.0
*/
class InvocationIndexParameterResolver implements ParameterResolver {

private final Integer invocationIndex;

InvocationIndexParameterResolver(int invocationIndex) {
this.invocationIndex = invocationIndex;
}

@Override
public boolean supports(ParameterContext parameterContext, ExtensionContext extensionContext) {
return isAnnotated(parameterContext.getParameter(), InvocationIndex.class);
}

@Override
public Integer resolve(ParameterContext parameterContext, ExtensionContext extensionContext) {
return this.invocationIndex;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ public Stream<TestTemplateInvocationContext> provide(ContainerExtensionContext c
Method templateMethod = Preconditions.notNull(context.getTestMethod().orElse(null),
"test method must not be null");

RepeatedTestInvocationContext testInvocationContext = new RepeatedTestInvocationContext(
createDisplayNameFormatter(templateMethod), context.getDisplayName());

// @formatter:off
return IntStream
.rangeClosed(1, getRepeatCount(templateMethod))
.mapToObj(n -> testInvocationContext);
.mapToObj(n -> new RepeatedTestInvocationContext(n, context.getDisplayName(),
createDisplayNameFormatter(templateMethod)));
// @formatter:on
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@

package org.junit.jupiter.engine.extension;

import static java.util.Collections.singletonList;

import java.util.List;

import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;

/**
Expand All @@ -20,17 +25,26 @@
*/
class RepeatedTestInvocationContext implements TestTemplateInvocationContext {

private final RepeatedTestDisplayNameFormatter formatter;
private final int repetition;
private final String displayName;
private final RepeatedTestDisplayNameFormatter formatter;

public RepeatedTestInvocationContext(RepeatedTestDisplayNameFormatter formatter, String displayName) {
this.formatter = formatter;
public RepeatedTestInvocationContext(int repetition, String displayName,
RepeatedTestDisplayNameFormatter formatter) {

this.repetition = repetition;
this.displayName = displayName;
this.formatter = formatter;
}

@Override
public String getDisplayName(int invocationIndex) {
return formatter.format(displayName, invocationIndex);
return this.formatter.format(this.displayName, invocationIndex);
}

@Override
public List<Extension> getAdditionalExtensions() {
return singletonList(new InvocationIndexParameterResolver(this.repetition));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.InvocationIndex;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.TestInfo;

Expand All @@ -27,10 +30,15 @@ class RepeatedTestTests {

private static int fortyTwo = 0;

@RepeatedTest(42)
void repeatedFortyTwoTimes(TestInfo testInfo) {
assertThat(testInfo.getDisplayName()).startsWith("repeatedFortyTwoTimes(TestInfo) :: repetition ");
fortyTwo++;
@BeforeEach
@AfterEach
void beforeAndAfterEach(TestInfo testInfo, @InvocationIndex int repetition) {
if (testInfo.getDisplayName().startsWith("repeatedOnce")) {
assertThat(repetition).isEqualTo(1);
}
else if (testInfo.getDisplayName().startsWith("repeatedFortyTwoTimes")) {
assertThat(repetition).isBetween(1, 42);
}
}

@AfterAll
Expand All @@ -53,6 +61,12 @@ void repeatedOnce(TestInfo testInfo) {
assertThat(testInfo.getDisplayName()).isEqualTo("repeatedOnce(TestInfo) :: repetition 1");
}

@RepeatedTest(42)
void repeatedFortyTwoTimes(TestInfo testInfo) {
assertThat(testInfo.getDisplayName()).startsWith("repeatedFortyTwoTimes(TestInfo) :: repetition ");
fortyTwo++;
}

@RepeatedTest(1)
void defaultDisplayName(TestInfo testInfo) {
assertThat(testInfo.getDisplayName()).isEqualTo("defaultDisplayName(TestInfo) :: repetition 1");
Expand Down Expand Up @@ -88,4 +102,16 @@ void defaultDisplayNameWithPatternIncludingDisplayNameAndIndex(TestInfo testInfo
"Repetition #1 for defaultDisplayNameWithPatternIncludingDisplayNameAndIndex(TestInfo)");
}

@RepeatedTest(value = 5, name = "{displayName}")
void injectInvocationIndexAsInt(TestInfo testInfo, @InvocationIndex int repetition) {
assertThat(testInfo.getDisplayName()).isEqualTo("injectInvocationIndexAsInt(TestInfo, int)");
assertThat(repetition).isBetween(1, 5);
}

@RepeatedTest(value = 5, name = "{displayName}")
void injectInvocationIndexAsInteger(TestInfo testInfo, @InvocationIndex Integer repetition) {
assertThat(testInfo.getDisplayName()).isEqualTo("injectInvocationIndexAsInteger(TestInfo, Integer)");
assertThat(repetition).isBetween(1, 5);
}

}

0 comments on commit d2e3a4d

Please sign in to comment.