developer tip

JUnit 4에서 특정 카테고리에 속하는 모든 테스트를 실행하는 방법

copycodes 2020. 11. 2. 08:08
반응형

JUnit 4에서 특정 카테고리에 속하는 모든 테스트를 실행하는 방법


JUnit 4.8에는 특정 종류의 테스트를 함께 그룹화 할 수있는 "카테고리"라는 멋진 새 기능이 포함되어 있습니다. 이는 예를 들어 느리고 빠른 테스트를 위해 별도의 테스트를 실행하는 데 매우 유용합니다. JUnit 4.8 릴리스 노트에 언급 된 내용 을 알고 있지만 특정 범주로 주석이 달린 모든 테스트를 실제로 실행하는 방법을 알고 싶습니다.

JUnit 4.8 릴리스 노트는 SuiteClasses 주석이 다음과 같이 실행할 특정 범주에서 테스트를 선택하는 예제 스위트 정의를 보여줍니다.

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
public class SlowTestSuite {
  // Will run A.b and B.c, but not A.a
}

SlowTests 카테고리의 모든 테스트를 어떻게 실행할 수 있는지 아는 사람이 있습니까? SuiteClasses 주석이 있어야하는 것 같습니다 ...


원하는 것을 달성 할 수있는 한 가지 가능한 방법을 찾았지만 JUnit의 일부가 아닌 ClassPathSuite 라이브러리에 의존하기 때문에 이것이 최상의 솔루션이라고 생각하지 않습니다.

다음과 같이 느린 테스트를위한 테스트 스위트를 정의합니다.

@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses( { AllTests.class })
public class SlowTestSuite {
}

AllTests 클래스는 다음과 같이 정의됩니다.

@RunWith(ClasspathSuite.class)
public class AllTests {
}

여기서 ClassPathSuite 프로젝트의 ClassPathSuite 클래스를 사용해야 했습니다. 테스트가있는 모든 클래스를 찾습니다.


그룹 (또는 JUnit이 호출하는 카테고리)과 관련하여 TestNG와 JUnit 간의 주요 차이점은 다음과 같습니다.

  • JUnit은 유형 (주석)이고 TestNG는 문자열입니다. 예를 들어 "데이터베이스 *"그룹에 속한 모든 테스트를 실행하는 등 테스트를 실행할 때 정규식을 사용할 수 있기를 원했기 때문에이 선택을했습니다. 또한 새 주석을 만들어야 할 때마다 새 주석을 만들어야합니다. 카테고리는 귀찮지 만 IDE가이 카테고리가 사용 된 위치를 즉시 알려줄 수 있다는 이점이 있습니다 (TestNG는 보고서에서이를 보여줍니다).

  • TestNG는 테스트가 실행되는 런타임 모델에서 정적 모델 (테스트 코드)을 매우 명확하게 구분합니다. "프론트 엔드"그룹을 먼저 실행 한 다음 "서블릿"을 실행하려면 아무것도 다시 컴파일하지 않고도이 작업을 수행 할 수 있습니다. JUnit은 주석에 그룹을 정의하고 이러한 범주를 러너에 대한 매개 변수로 지정해야하기 때문에 일반적으로 다른 범주 집합을 실행하려고 할 때마다 코드를 다시 컴파일해야하므로 제 생각에는 목적을 무효화합니다.


Kaitsu 솔루션의 한 가지 단점 은 프로젝트의 모든 테스트를 실행할 때 Eclipse 가 테스트를 두 번 실행하고 SlowTests를 세 번 실행한다는 것입니다. 이것은 Eclipse가 모든 테스트, AllTests 제품군, SlowTestSuite를 차례로 실행하기 때문입니다.

다음은 특정 시스템 속성이 설정되지 않은 경우 제품군을 건너 뛰도록 Kaitsu 솔루션 테스트 실행기의 하위 클래스를 만드는 솔루션입니다. 부끄러운 해킹이지만 지금까지 내가 생각 해낸 모든 것.

@RunWith(DevFilterClasspathSuite.class)
public class AllTests {}

.

@RunWith(DevFilterCategories.class)
@ExcludeCategory(SlowTest.class)
@SuiteClasses(AllTests.class)
public class FastTestSuite
{
}

.

public class DevFilterCategories extends Suite
{
    private static final Logger logger = Logger
        .getLogger(DevFilterCategories.class.getName());
    public DevFilterCategories(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError {
        super(suiteClass, builder);
        try {
            filter(new CategoryFilter(getIncludedCategory(suiteClass),
                    getExcludedCategory(suiteClass)));
            filter(new DevFilter());
        } catch (NoTestsRemainException e) {
            logger.info("skipped all tests");
        }
        assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription());
    }

    private Class<?> getIncludedCategory(Class<?> klass) {
        IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class);
        return annotation == null ? null : annotation.value();
    }

    private Class<?> getExcludedCategory(Class<?> klass) {
        ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
        return annotation == null ? null : annotation.value();
    }

    private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
        if (!canHaveCategorizedChildren(description))
            assertNoDescendantsHaveCategoryAnnotations(description);
        for (Description each : description.getChildren())
            assertNoCategorizedDescendentsOfUncategorizeableParents(each);
    }

    private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {           
        for (Description each : description.getChildren()) {
            if (each.getAnnotation(Category.class) != null)
                throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods.");
            assertNoDescendantsHaveCategoryAnnotations(each);
        }
    }

    // If children have names like [0], our current magical category code can't determine their
    // parentage.
    private static boolean canHaveCategorizedChildren(Description description) {
        for (Description each : description.getChildren())
            if (each.getTestClass() == null)
                return false;
        return true;
    }
}

.

public class DevFilterClasspathSuite extends ClasspathSuite
{
    private static final Logger logger = Logger
        .getLogger(DevFilterClasspathSuite.class.getName());
    public DevFilterClasspathSuite(Class<?> suiteClass, RunnerBuilder builder) 
        throws InitializationError {
        super(suiteClass, builder);
        try
        {
            filter(new DevFilter());
        } catch (NoTestsRemainException e)
        {
            logger.info("skipped all tests");
        }
    }
}

.

public class DevFilter extends Filter
{
    private static final String RUN_DEV_UNIT_TESTS = "run.dev.unit.tests";

    @Override
    public boolean shouldRun(Description description)
    {
        return Boolean.getBoolean(RUN_DEV_UNIT_TESTS);
    }

    @Override
    public String describe()
    {
        return "filter if "+RUN_DEV_UNIT_TESTS+" system property not present";
    }
}

따라서 FastTestSuite 시작 관리자에서 VM 인수에 -Drun.dev.unit.tests = true를 추가하면됩니다. (이 솔루션은 느린 테스트 스위트 대신 빠른 테스트 스위트를 참조합니다.)


@Suite.SuiteClasses주석에 모든 테스트를 명시 적으로 지정하지 않고 분류 된 테스트를 실행하려면 자신 만의 Suite 구현을 제공 할 수 있습니다. 예를 들어 a org.junit.runners.ParentRunner를 확장 할 수 있습니다. 에서 제공하는 클래스 배열을 사용하는 대신 @Suite.SuiteClasses새로운 구현은 클래스 경로에서 분류 된 테스트를 검색해야합니다.

이러한 접근 방식의 예 로이 프로젝트참조하십시오 . 용법:

@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class})
@BasePackage(name = "some.package")
@RunWith(CategorizedSuite.class)
public class CategorizedSuiteWithSpecifiedPackage {

}

나는 정확히 당신의 문제가 무엇인지 잘 모르겠습니다.

Just add all the tests to a suite (or hirachy of suites). Then use the Categories Runner and Include/ExcludeCategory annotation, to specify the categories you want to run.

A good idea might be to have one suite containing all the tests, and a couple of seperate suites referring to the first one, specifying the different set of Categories you neeed.


Not a direct answer to your problem, but maybe the general approach could be improved...

Why are your tests slow? Maybe the set-up lasts long (database, I/O etc.), maybe the tests are testing too much? If this is the case I would seperate the real unit-tests from the "long-running" ones, which often indeed are integration tests.

In my setups I have staging env, where unit-tests are run often and integration-tests constantly but more rarely (e.g. after each commit in version control). I have never worked with grouping for unit tests, because they should be loosely coupled alltogether. I only work with grouping and relationship of test-cases in integration-test setups (but with TestNG).

But good to know that JUnit 4.8 introduced some grouping features.

참고URL : https://stackoverflow.com/questions/2176570/how-to-run-all-tests-belonging-to-a-certain-category-in-junit-4

반응형