JUnit 5 在测试中排除标记的测试方法 class
JUnit 5 exlude tagged test methods within test class
@SpringBootTest
@AutoConfigureMockMvc
@ExcludeTags({"no"})
public class MyClassTest {
@Test
public void test1() {
}
@Test
@Tag("no")
public void test2() {
}
...
}
@RunWith(JUnitPlatform.class)
@SelectClasses({MyClassTest.class})
@IncludeTags({"no"})
public class MyClassTestSuiteTest {
}
有一个 Spring Boot 2.3.1
项目并测试一些 REST 控制器,在测试中 class 一些测试方法被标记,不应 运行,当 MyClassTest
是 运行。带注释的方法在测试套件中为 运行(带有 @IncludeTags("no")
。JUnit 5.6.2
。
对于测试套件,我不确定它 @RunWith
必须用于测试套件,还是 JUnit 5 @ExtendWith
是正确的?事实上,如果没有必要,我不想混用 JUnit 4 和 5,坚持使用 JUnit 5。
有没有一种方法可以简单地通过注释或类似方式进行配置,以便在 MyClassTest
为 运行 时不 运行 标记的方法?就像 @ExcludeTags
用于测试套件,但这不适用于示例中的 class。
也许可以创建两个测试套件,一个 @ExludeTags("no")
,一个 @IncludeTags("no")
。但是,如何完全防止 MyClassTest
它 运行?
我不想在特定 IDE 中创建一些 运行 配置。首选方法是使用注释或类似方法。也许 Maven 配置也足够了。
也许可以通过某些标准评估避免在测试方法级别执行特定测试方法,如果执行的测试 class 是 MyClassTest,则不要 运行 该测试方法。
有趣的是,我不能简单地将 @RunWith(JUnitPlatform.class)
替换为 @ExtendWith(JUnitPlatform.class)
,因为类型不兼容。使用 @ExtendWith(SpringExtension.class)
不会给我 运行 和 class 的可能性(例如,右键单击 class 名称,无法进入 Run/Debug) .但是 @ExtendWith
取代了 JUnit 5 中的 @RunWith
,对 运行 测试套件使用什么扩展?
创建执行条件ExcludeTagsCondition
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.util.AnnotationUtils;
public class ExcludeTagsCondition implements ExecutionCondition {
private static final ConditionEvaluationResult ENABLED_IF_EXCLUDE_TAG_IS_INVALID =
ConditionEvaluationResult.enabled(
"@ExcludeTags does not have a valid tag to exclude, all tests will be run");
private static Set<String> tagsThatMustBeIncluded = new HashSet<>();
public static void setMustIncludeTags(final Set<String> tagsThatMustBeIncluded) {
ExcludeTagsCondition.tagsThatMustBeIncluded = new HashSet<>(tagsThatMustBeIncluded);
}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(
ExtensionContext context) {
final AnnotatedElement element = context
.getElement()
.orElseThrow(IllegalStateException::new);
final Optional<Set<String>> tagsToExclude = AnnotationUtils.findAnnotation(
context.getRequiredTestClass(),
ExcludeTags.class
)
.map(a ->
Arrays.asList(a.value())
.stream()
.filter(t -> !tagsThatMustBeIncluded.contains(t))
.collect(Collectors.toSet())
);
if (!tagsToExclude.isPresent() || tagsToExclude.get().stream()
.allMatch(s -> (s == null) || s.trim().isEmpty())) {
return ENABLED_IF_EXCLUDE_TAG_IS_INVALID;
}
final Optional<String> tag = AnnotationUtils.findAnnotation(element, Tag.class)
.map(Tag::value);
if (tagsToExclude.get().contains(tag.map(String::trim).orElse(""))) {
return ConditionEvaluationResult
.disabled(String.format(
"test method \"%s\" has tag \"%s\" which is on the @ExcludeTags list \"[%s]\", test will be skipped",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.get(),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
return ConditionEvaluationResult.enabled(
String.format(
"test method \"%s\" has tag \"%s\" which is not on the @ExcludeTags list \"[%s]\", test will be run",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.orElse("<no tag present>"),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
}
创建注释@ExcludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@ExtendWith(ExcludeTagsCondition.class)
public @interface ExcludeTags {
String[] value();
}
考试中
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@ExcludeTags({"foo", "bar"})
@SpringBootTest
class AppTest {
@Test
@Tag("foo")
void test1() {
System.out.println("test1");
}
@Test
@Tag("bar")
void test2() {
System.out.println("test2");
}
@Test
@Tag("baz")
void test3() {
System.out.println("test3");
}
}
当您 运行 测试时,您应该看到以下输出:
test method "test1" has tag "foo" which is on the @ExcludeTags list "[bar,foo]", test will be skipped
test method "test2" has tag "bar" which is on the @ExcludeTags list "[bar,foo]", test will be skipped
test3
你的测试 运行ner 应该显示 1 个测试通过和 2 个跳过。
现在开始测试套件:
创建注释@MustIncludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface MustIncludeTags {
String[] value();
}
现在像这样设置你的测试套件:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.runner.RunWith;
@RunWith(JUnitPlatform.class)
@SelectClasses({MyTestSuite.SetupTests.class, AppTest.class})
@MustIncludeTags({"foo", "bar"})
public class MyTestSuite {
public static class SetupTests {
@BeforeAll
public static void beforeClass() {
ExcludeTagsCondition.setMustIncludeTags(
Optional.ofNullable(MyTestSuite.class.getAnnotation(MustIncludeTags.class))
.map(MustIncludeTags::value)
.map(Arrays::asList)
.orElse(new ArrayList<>())
.stream()
.collect(Collectors.toSet())
);
}
@Disabled
@Test
void testDummy() {
// this test needs to be present for the beforeAll to run
}
}
}
当您 运行 带有 @MustIncludeTags
的测试套件时,@ExcludedTags
被覆盖。
从下面的测试执行可以看出:
@SpringBootTest
@AutoConfigureMockMvc
@ExcludeTags({"no"})
public class MyClassTest {
@Test
public void test1() {
}
@Test
@Tag("no")
public void test2() {
}
...
}
@RunWith(JUnitPlatform.class)
@SelectClasses({MyClassTest.class})
@IncludeTags({"no"})
public class MyClassTestSuiteTest {
}
有一个 Spring Boot 2.3.1
项目并测试一些 REST 控制器,在测试中 class 一些测试方法被标记,不应 运行,当 MyClassTest
是 运行。带注释的方法在测试套件中为 运行(带有 @IncludeTags("no")
。JUnit 5.6.2
。
对于测试套件,我不确定它 @RunWith
必须用于测试套件,还是 JUnit 5 @ExtendWith
是正确的?事实上,如果没有必要,我不想混用 JUnit 4 和 5,坚持使用 JUnit 5。
有没有一种方法可以简单地通过注释或类似方式进行配置,以便在 MyClassTest
为 运行 时不 运行 标记的方法?就像 @ExcludeTags
用于测试套件,但这不适用于示例中的 class。
也许可以创建两个测试套件,一个 @ExludeTags("no")
,一个 @IncludeTags("no")
。但是,如何完全防止 MyClassTest
它 运行?
我不想在特定 IDE 中创建一些 运行 配置。首选方法是使用注释或类似方法。也许 Maven 配置也足够了。
也许可以通过某些标准评估避免在测试方法级别执行特定测试方法,如果执行的测试 class 是 MyClassTest,则不要 运行 该测试方法。
有趣的是,我不能简单地将 @RunWith(JUnitPlatform.class)
替换为 @ExtendWith(JUnitPlatform.class)
,因为类型不兼容。使用 @ExtendWith(SpringExtension.class)
不会给我 运行 和 class 的可能性(例如,右键单击 class 名称,无法进入 Run/Debug) .但是 @ExtendWith
取代了 JUnit 5 中的 @RunWith
,对 运行 测试套件使用什么扩展?
创建执行条件ExcludeTagsCondition
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.util.AnnotationUtils;
public class ExcludeTagsCondition implements ExecutionCondition {
private static final ConditionEvaluationResult ENABLED_IF_EXCLUDE_TAG_IS_INVALID =
ConditionEvaluationResult.enabled(
"@ExcludeTags does not have a valid tag to exclude, all tests will be run");
private static Set<String> tagsThatMustBeIncluded = new HashSet<>();
public static void setMustIncludeTags(final Set<String> tagsThatMustBeIncluded) {
ExcludeTagsCondition.tagsThatMustBeIncluded = new HashSet<>(tagsThatMustBeIncluded);
}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(
ExtensionContext context) {
final AnnotatedElement element = context
.getElement()
.orElseThrow(IllegalStateException::new);
final Optional<Set<String>> tagsToExclude = AnnotationUtils.findAnnotation(
context.getRequiredTestClass(),
ExcludeTags.class
)
.map(a ->
Arrays.asList(a.value())
.stream()
.filter(t -> !tagsThatMustBeIncluded.contains(t))
.collect(Collectors.toSet())
);
if (!tagsToExclude.isPresent() || tagsToExclude.get().stream()
.allMatch(s -> (s == null) || s.trim().isEmpty())) {
return ENABLED_IF_EXCLUDE_TAG_IS_INVALID;
}
final Optional<String> tag = AnnotationUtils.findAnnotation(element, Tag.class)
.map(Tag::value);
if (tagsToExclude.get().contains(tag.map(String::trim).orElse(""))) {
return ConditionEvaluationResult
.disabled(String.format(
"test method \"%s\" has tag \"%s\" which is on the @ExcludeTags list \"[%s]\", test will be skipped",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.get(),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
return ConditionEvaluationResult.enabled(
String.format(
"test method \"%s\" has tag \"%s\" which is not on the @ExcludeTags list \"[%s]\", test will be run",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.orElse("<no tag present>"),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
}
创建注释@ExcludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@ExtendWith(ExcludeTagsCondition.class)
public @interface ExcludeTags {
String[] value();
}
考试中
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@ExcludeTags({"foo", "bar"})
@SpringBootTest
class AppTest {
@Test
@Tag("foo")
void test1() {
System.out.println("test1");
}
@Test
@Tag("bar")
void test2() {
System.out.println("test2");
}
@Test
@Tag("baz")
void test3() {
System.out.println("test3");
}
}
当您 运行 测试时,您应该看到以下输出:
test method "test1" has tag "foo" which is on the @ExcludeTags list "[bar,foo]", test will be skipped
test method "test2" has tag "bar" which is on the @ExcludeTags list "[bar,foo]", test will be skipped
test3
你的测试 运行ner 应该显示 1 个测试通过和 2 个跳过。
现在开始测试套件:
创建注释@MustIncludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface MustIncludeTags {
String[] value();
}
现在像这样设置你的测试套件:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.runner.RunWith;
@RunWith(JUnitPlatform.class)
@SelectClasses({MyTestSuite.SetupTests.class, AppTest.class})
@MustIncludeTags({"foo", "bar"})
public class MyTestSuite {
public static class SetupTests {
@BeforeAll
public static void beforeClass() {
ExcludeTagsCondition.setMustIncludeTags(
Optional.ofNullable(MyTestSuite.class.getAnnotation(MustIncludeTags.class))
.map(MustIncludeTags::value)
.map(Arrays::asList)
.orElse(new ArrayList<>())
.stream()
.collect(Collectors.toSet())
);
}
@Disabled
@Test
void testDummy() {
// this test needs to be present for the beforeAll to run
}
}
}
当您 运行 带有 @MustIncludeTags
的测试套件时,@ExcludedTags
被覆盖。
从下面的测试执行可以看出: