JUnit 5 исключает тегированные методы тестирования в тестовом классе - PullRequest
0 голосов
/ 16 июня 2020
@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. Можно было бы использовать аннотации или аналогичный. тогда не беги этот метод тестирования.

Интересно то, что я не могу заменить @RunWith(JUnitPlatform.class) просто @ExtendWith(JUnitPlatform.class), поскольку существует несовместимость типов. Использование @ExtendWith(SpringExtension.class) не дает мне возможности запустить класс (например, щелкнув правой кнопкой мыши имя класса, нет записи в Run / Debug). Но @ExtendWith заменяет @RunWith в JUnit 5, какое расширение использовать для запуска набора тестов?

1 Ответ

1 голос
/ 16 июня 2020

Создать условие выполнения 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 пропущенных.

enter image description here

Now for your test suite:

Create an annotation @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 переопределяются.

Как видно из следующего выполнения теста :

введите описание изображения здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...