【问题标题】:JUnit 5 exlude tagged test methods within test classJUnit 5 排除测试类中的标记测试方法
【发布时间】:2020-06-16 07:16:21
【问题描述】:
@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 控制器,在测试类中,一些测试方法被标记,并且在运行MyClassTest 时不应运行。带注释的方法在测试套件中运行(使用@IncludeTags("no")JUnit 5.6.2

对于测试套件,我不确定 @RunWith 是否必须用于测试套件,还是 JUnit 5 @ExtendWith 是正确的?其实如果没有必要,我不想混用 JUnit 4 和 5,坚持 JUnit 5。

有没有办法简单地通过注释或类似的方式进行配置,在运行MyClassTest 时不运行标记的方法?就像 @ExcludeTags 用于测试套件一样,但这不适用于示例中的类。

也许可以创建两个测试套件,一个带有@ExludeTags("no"),一个带有@IncludeTags("no")。但是,如何防止 MyClassTest 它运行呢?

我不想在特定的 IDE 中创建一些运行配置。首选方法是使用注释或类似方法。也许一个 Maven 配置也足够了。

也许通过某些标准评估可以避免在测试方法级别执行特定测试方法,如果执行的测试类是 MyClassTest,则不要运行该测试方法。

有趣的是,我不能简单地用@ExtendWith(JUnitPlatform.class) 替换@RunWith(JUnitPlatform.class),因为存在类型不兼容。使用@ExtendWith(SpringExtension.class) 无法让我运行该类(例如,右键单击类名,没有运行/调试条目)。但是 @ExtendWith 在 JUnit 5 中替换了 @RunWith,使用什么扩展来运行测试套件?

【问题讨论】:

  • 为什么不用@Ignore
  • @Ignore 在哪个级别?类还是方法?如果我将它添加到一个方法上,另外我必须删除'@Test',否则它仍然会被执行。
  • 您不需要删除@Test 注释。您可以在类(它将忽略整个类测试)或方法(仅忽略那些方法)上使用@Ignore
  • 在类上添加“@Ignore”显示了两种不同的执行,一种用于未运行测试类的 JUnit Vintage,一种用于仍在运行的 JUnit Jupiter。在测试方法上添加@Ignore,仍然运行测试方法。有不同的@Ignore吗?同样在测试方法上使用“@Ignore”,测试方法不会在测试套件中运行。
  • 尝试使用@Disabled(自 JUnit 5 起已过时)。 @忽略参考org.junit.Ignorelink

标签: spring-boot junit5


【解决方案1】:

创建执行条件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

您的测试运行程序应该显示 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 将被覆盖。

从下面的测试执行中可以看出:

【讨论】:

  • 我更新了我的问题,你的回答还有效吗?另外,在使用 Junit 5 创建测试套件时,“@RunWith”是否正确,还是应该使用“@ExtendWith”?
  • @neblaz 更新了答案,刚刚用 Junit 5.5.1 测试过
  • 会试一试,但您是否还创建了一个运行带注释的方法的测试套件?该类应排除一些标记方法,但测试套件应运行它们。请参阅我在问题中的示例。
  • 我刚试了一下,指定的标签在MyClassTest类中没有执行,没问题。但是它们也没有在测试套件 MyClassTestSuiteTest 中执行,这是不希望的。
  • 首先要了解,是否必须混合使用 JUnit 4 和 5 工件,还是可以全部使用 JUnit 5 工件来完成?据我了解,'@RunWith'(也称为'@BeforeClass')是 JUnit 5 之前的旧注释,它必须与测试套件一起使用还是可以使用'@ExtendWith'?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多