동일한 junit 테스트를 반복해서 실행하는 쉬운 방법?


121

제목에서 알 수 있듯이 Eclipse를 사용하여 JUnit 4.x 테스트를 연속으로 여러 번 실행하는 간단한 방법을 찾고 있습니다.

예를 들어 동일한 테스트를 10 번 연속 실행하고 결과를보고합니다.

우리는 이미이 작업을 수행하는 복잡한 방법을 가지고 있지만, 제가 고치려고했던 비정상적인 테스트가 고정 된 상태로 유지 될 수 있도록 간단한 방법을 찾고 있습니다.

이상적인 솔루션은 내가 알지 못하는 Eclipse 플러그인 / 설정 / 기능입니다.


5
나는 당신이 왜 이것을하고 싶어하는지에 대해 매우 궁금합니다.
Buhb

저는 큰 블랙 박스 테스트를 실행하고 있으며 작은 변경을 수행했으며 이것이 이전에 불안정한 테스트의 안정성에 어떤 영향을 주 었는지 확인하고 싶습니다.
Stefan Thyberg

실제로는 실패 할 때까지 실행되기를 원하고 여러 번 실행하기를 원한다는 점을 제외하면 내가 얻는 답변에 영향을 미칠 수 있습니다.
Stefan Thyberg

4
그렇지 않다면 @Test (invocationCount = 10)를 사용할 수 있기 때문에 TestNG에 반대합니까?
Robert Massaioli 2011 년

1
나는 TestNG를 "반대"하지 않았고, 우리는 그 프로젝트에서 그것을 사용하지 않았습니다.
Stefan Thyberg 2011

답변:


123

이를 수행하는 가장 쉬운 방법 (최소한의 새 코드 필요)은 테스트를 매개 변수화 된 테스트로 실행하는 것 @RunWith(Parameterized.class)입니다. 이렇게하면 프레임 워크가 테스트를 10 번 실행합니다.

이 테스트는 클래스에서 유일한 테스트 여야합니다. 그렇지 않으면 모든 테스트 메서드가 클래스에서 10 번 실행되어야합니다.

다음은 그 예입니다.

@RunWith(Parameterized.class)
public class RunTenTimes {

    @Parameterized.Parameters
    public static Object[][] data() {
        return new Object[10][0];
    }

    public RunTenTimes() {
    }

    @Test
    public void runsTenTimes() {
        System.out.println("run");
    }
}

위의 경우 매개 변수없는 생성자로도 할 수 있지만 프레임 워크 작성자가 의도 한 것인지 아니면 향후 중단 될 것인지 확신 할 수 없습니다.

자체 러너를 구현하는 경우 러너가 테스트를 10 번 실행하도록 할 수 있습니다. 타사 실행기를 사용하는 경우 4.7에서는 새 @Rule주석을 사용 하고 MethodRule인터페이스를 구현 하여 문을 가져 와서 for 루프에서 10 번 실행하도록 할 수 있습니다. 이 방법의 현재 단점은 것입니다 @Before그리고 @After한 번만 실행하세요. 이것은 JUnit의 다음 버전에서 변경 될 가능성이 있지만 (는 @Before이후에 실행될 것입니다 @Rule),하지만 객체의 동일한 인스턴스 (실행자에게는 사실이 아닌 것)에서 작동하게 될 것입니다 Parameterized. 이것은 클래스를 실행하는 주자가 @Rule주석 을 올바르게 인식한다고 가정합니다 . JUnit 실행자에게 위임하는 경우에만 해당됩니다.

@Rule주석을 인식하지 않는 사용자 지정 러너로 실행하는 경우 해당 러너에게 적절하게 위임하고 10 번 실행하는 자체 러너를 작성해야하는 데 정말 고착됩니다.

이 문제를 잠재적으로 해결할 수있는 다른 방법 (예 : Theories 러너)이 있지만 모두 러너가 필요합니다. 불행히도 JUnit은 현재 러너 레이어를 지원하지 않습니다. 그것은 다른 주자를 묶는 주자입니다.


2
불행히도 나는 이미 다른 주자와 함께 @RunWith를 실행하고 있지만 그렇지 않으면 이상적인 솔루션이 될 것입니다.
Stefan Thyberg

예, 이것이 제가 갖고 싶은 해결책이며 대부분의 사람들에게 가장 좋을 것이므로 계속해서 대답을 받아 들일 것입니다.
Stefan Thyberg

덜 해키가없는 대체 솔루션은 다음을 참조하십시오. stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D

좋은 해결책! 데이터 메서드가 Iterable of Arrays를 반환해야한다는 예외가 발생했습니다. 그에 따라 수정했습니다 : @ Parameterized.Parameters public static Iterable <Object []> data () {return Arrays.asList (new Object [20] [0]); }
nadre

1
JUnit 5에 대한 이 답변 을 링크 해 주 시겠습니까? 이는 5 JUnit을 첨가 하였다 요청한 기능 설명
Marcono1234

100

IntelliJ를 사용하면 테스트 구성에서이를 수행 할 수 있습니다. 이 창을 열면 원하는 횟수만큼 테스트를 실행하도록 선택할 수 있습니다.

여기에 이미지 설명 입력

테스트를 실행하면 intellij는 지정한 횟수만큼 선택한 모든 테스트를 실행합니다.

624 개의 테스트를 10 번 실행 한 예 : 여기에 이미지 설명 입력


4
완벽합니다. 이제이 일식 방법을 가리킬 수 있다면 OP의 질문에 대한 답이 될 것입니다
khal

특정 도구에 의존하여 실제 논리 또는 요구 사항을 호스팅하는 것은 반 패턴입니다.
Mickael 19

1
@Mickael 테스트를 N 번 반복하는 것은 일반적으로 테스트의 요구 사항이 아닙니다. 실제로 테스트는 결정적이어야하므로 반복 횟수에 관계없이 항상 동일한 결과를 생성해야합니다. 당신이 말하는 안티 패턴을 설명해 주시겠습니까?
smac89

테스트를 반복하는 것이 한 개발자에게 유용했다면 다른 개발자에게도 유용 할 것입니다. 따라서 테스트 런타임과 코드가 반복을 가능하게하는 논리를 호스팅 할 수 있다면 노력과 솔루션을 인수 분해하고 기여자가 동일한 결과로 원하는 도구를 사용할 수 있도록 허용하므로 선호되어야합니다. 재사용 가능한 로직을 코드에 넣을 수있을 때 IDE / 개발자 영역에 배치하는 것은 일종의 인수 분해가 아닙니다.
Mickael 19

68

나는 Spring의 반복 주석이 그런 종류의 일에 유용하다는 것을 발견했습니다.

@Repeat(value = 10)

최신 (Spring Framework 4.3.11.RELEASE API) 문서 :


46
테스트 프레임 워크를 변경하는 것은 쉬운 방법이 아닙니다.
Stefan Thyberg

3
테스트 프레임 워크를 변경할 필요가 없습니다. JUnit에서 잘 작동합니다. 가장 큰 단점은 JUnit이 여전히 단일 테스트로 간주한다는 것입니다. 따라서 처음 중단되면 실행이 중지됩니다. 이미 스프링을 사용하지 않는 경우, 다음은 ... 아마 당신이 가고 싶은 방법은 아니다
tveon은

(봄 부팅을 통해 봄 4.3.6 1.5.1) 나를 위해 작동하지 않는 것
데이비드 Tonhofer

스프링 부트 2.1.6 및 junit 5에서 작동하지 않음
jo-

스프링 부트 2와 완벽하게 작동합니다. 포스터의 '봄의 단위 테스트'링크에 따라 @RunWith (SpringRunner :: class)를 추가하는 것을 잊지 마십시오!
Agoston Horvath

33

다음 리소스에서 영감을 얻었습니다.

다음과 @Repeat같이 주석을 만들고 사용 합니다.

public class MyTestClass {

    @Rule
    public RepeatRule repeatRule = new RepeatRule();

    @Test
    @Repeat(10)
    public void testMyCode() {
        //your test code goes here
    }
}

Repeat.java

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention( RetentionPolicy.RUNTIME )
@Target({ METHOD, ANNOTATION_TYPE })
public @interface Repeat {
    int value() default 1;
}

RepeatRule.java

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class RepeatRule implements TestRule {

    private static class RepeatStatement extends Statement {
        private final Statement statement;
        private final int repeat;    

        public RepeatStatement(Statement statement, int repeat) {
            this.statement = statement;
            this.repeat = repeat;
        }

        @Override
        public void evaluate() throws Throwable {
            for (int i = 0; i < repeat; i++) {
                statement.evaluate();
            }
        }

    }

    @Override
    public Statement apply(Statement statement, Description description) {
        Statement result = statement;
        Repeat repeat = description.getAnnotation(Repeat.class);
        if (repeat != null) {
            int times = repeat.value();
            result = new RepeatStatement(statement, times);
        }
        return result;
    }
}

PowerMock

이 솔루션을 @RunWith(PowerMockRunner.class)에서 사용하려면 Powermock 1.6.5 ( 패치 포함) 업데이트해야합니다 .


예. 테스트를 어떻게 실행하고 있습니까?
R. Oosterholt 2015

나는 이클립스를 직접 사용하지 않습니다. junut 4 테스트 러너를 사용하고 있지 않을까요? ( "테스트 구성 사용자 지정"문서 참조 )
R. Oosterholt 2015

29

JUnit 5에서는 @RepeatedTest 주석을 사용 하여이 문제를 해결할 수있었습니다 .

@RepeatedTest(10)
public void testMyCode() {
    //your test code goes here
}

참고 @Test주석과 함께 사용할 수 없습니다 @RepeatedTest.


아직 릴리스 버전이 없다는 점에 주목하기 위해 매우 유망한 것 같습니다.
9ilsdx 9rvj 0lo

1
JUnit 5는 몇 주 전에 GA에 도달했습니다.
mkobit

11

문제가있는 경우 :

@Test
void itWorks() {
    // stuff
}

@Test
void itWorksRepeatably() {
    for (int i = 0; i < 10; i++) {
        itWorks();
    }
}

각 값 배열을 테스트하는 경우와 달리 어떤 실행이 실패했는지 특별히 신경 쓰지 않습니다.

코드에서 수행 할 수있는 작업을 구성 또는 주석에서 수행 할 필요가 없습니다.


2
여러 테스트를 일반 단위 테스트로 실행하고 각 테스트에 대한 추적 및 상태를 얻고 싶습니다.
Stefan Thyberg

24
이 경우 "@Before"및 "@After"는 실행되지 않습니다
Bogdan

3
이것은 내 문제 @BeforeitWorks()해결 하기 전에 주석 이 달린 메서드 를 수동으로 호출하는 것과 함께 .
João Neves 2015 년

DRY 개념을 알고 있습니까? en.wikipedia.org/wiki/Don%27t_repeat_yourself 루프를 모든 곳에 복사하는 대신 몇 가지 설정을하는 것이 좋습니다.
Kikiwa

이 답변의 편집 대기열이 꽉 찼습니다. 그러므로 나는 그것을 주석에 넣을 것이다 : JUnit4의 경우 테스트는 공개되어야한다.
Richard Jessop

7

이것은 나를 위해 훨씬 쉽게 작동합니다.

public class RepeatTests extends TestCase {

    public static Test suite() {
        TestSuite suite = new TestSuite(RepeatTests.class.getName());

        for (int i = 0; i < 10; i++) {              
        suite.addTestSuite(YourTest.class);             
        }

        return suite;
    }
}

다른 프레임 워크를 사용하고 실제로 JUnit을 3 (안드로이드에 매우 중요) 작동하지 않는 최고
블라디미르 이바노프을

1
JUnit4를 사용한 구현은 Runner로 수행 할 수 있습니다. public class RepeatRunner extends BlockJUnit4ClassRunner { public RepeatRunner(Class klass) throws InitializationError { super(klass); } @Override public void run(final RunNotifier notifier) { for (int i = 0; i < 10; i++) { super.run(notifier); } } }적어도 Eclipse JUnit 플러그인에서는 다음과 같은 결과를 얻을 수 있습니다. "10/1 테스트 통과"
Peter Wippermann

7

에 간헐적 주석있다 템퍼 - fugit 의 JUnit과 4.7의 작동 라이브러리 @Rule테스트를 여러 번 또는과 반복은 @RunWith.

예를 들면

@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {

   private static int testCounter = 0;

   @Test
   @Intermittent(repition = 99)
   public void annotatedTest() {
      testCounter++;
   }
}

테스트가 실행 된 후 (에서 IntermittentTestRunner 사용 @RunWith) testCounter는 99와 같습니다.


예, 여기에서 같은 문제입니다. 이미 다른 주자를 사용하고 있으므로이 주자를 사용할 수 없습니다. 좋은 생각입니다.
Stefan Thyberg

네, RunWith와 같은 문제가 있습니다 ...가는 동안 tempus-fugit을 약간 조정하여 반복적으로 달리고 싶을 때 러너 대신 @Rule을 사용할 수 있습니다. 간헐적 인 대신 @Repeating으로 표시합니다. 규칙 버전은 @ Before / @ Afters를 실행하지 않습니다. 자세한 내용은 tempus-fugit.googlecode.com/svn/site/documentation/… (아래로 스크롤하여로드 / 소크 테스트)를 참조하세요.
Toby

0

이런 종류의 테스트를 수행 할 수있는 모듈을 빌드합니다. 그러나 그것은 반복적으로 만 집중되는 것이 아닙니다. 그러나 일부 코드는 스레드로부터 안전합니다.

https://github.com/anderson-marques/concurrent-testing

Maven 종속성 :

<dependency>
    <groupId>org.lite</groupId>
    <artifactId>concurrent-testing</artifactId>
    <version>1.0.0</version>
</dependency>

사용 예 :

package org.lite.concurrent.testing;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import ConcurrentTest;
import ConcurrentTestsRule;

/**
 * Concurrent tests examples
 */
public class ExampleTest {

    /**
     * Create a new TestRule that will be applied to all tests
     */
    @Rule
    public ConcurrentTestsRule ct = ConcurrentTestsRule.silentTests();

    /**
     * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
     */
    @Test
    @ConcurrentTest(requests = 20, threads = 10)
    public void testConcurrentExecutionSuccess(){
        Assert.assertTrue(true);
    }

    /**
     * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
     */
    @Test
    @ConcurrentTest(requests = 200, threads = 10, timeoutMillis = 100)
    public void testConcurrentExecutionSuccessWaitOnly100Millissecond(){
    }

    @Test(expected = RuntimeException.class)
    @ConcurrentTest(requests = 3)
    public void testConcurrentExecutionFail(){
        throw new RuntimeException("Fail");
    }
}

이것은 오픈 소스 프로젝트입니다. 자유롭게 개선하십시오.


0

메인 메소드에서 JUnit 테스트를 실행하고 필요한만큼 반복 할 수 있습니다.

package tests;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.Result;

public class RepeatedTest {

    @Test
    public void test() {
        fail("Not yet implemented");
    }

    public static void main(String args[]) {

        boolean runForever = true;

        while (runForever) {
            Result result = org.junit.runner.JUnitCore.runClasses(RepeatedTest.class);

            if (result.getFailureCount() > 0) {
                runForever = false;
               //Do something with the result object

            }
        }

    }

}

0

이것은 본질적으로 Yishai가 위에서 제공 한 답변이며 Kotlin으로 다시 작성되었습니다.

@RunWith(Parameterized::class)
class MyTest {

    companion object {

        private const val numberOfTests = 200

        @JvmStatic
        @Parameterized.Parameters
        fun data(): Array<Array<Any?>> = Array(numberOfTests) { arrayOfNulls<Any?>(0) }
    }

    @Test
    fun testSomething() { }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.