标记为运行太长的JUnit测试失败

时间:2014-09-29 19:49:17

标签: java maven junit

我想停止并标记为运行junit测试失败(在Maven 3版本中执行)。我知道有三种方法:

1)使用带有超时参数的测试注释:

@Test(timeout=100)
public void testWithTimeout() {
    ...
}

2)使用规则注释:

@Rule
public Timeout globalTimeout = new Timeout(100);

3)使用以下选项配置maven-surefire-plugin:

forkedProcessTimeoutInSeconds=1
reuseForks=false

线索是1)和2)需要改变每个测试(当你有数千个时它会受伤)。 3)解决方案是不可接受的,因为在许多模块中,第一次测试开始测试所使用的上下文 - 测试性能会急剧下降。

您对此有何其他想法?任何棘手的解决方案(不涉及修补JUnit :))?

4 个答案:

答案 0 :(得分:2)

您可以尝试通过编写扩展BlockJunit4ClassRunner的类来定义自己的测试运行器,如果测试执行超出定义的超时,则会失败。 然后使用@RunWith(CustomizedTestRunner.class)注释您的测试类 您仍然需要修改类,但可以在单个位置指定超时值。

答案 1 :(得分:0)

实际上,超时规则对类中的所有测试方法应用相同的超时。

如果您不想将其添加到每个Test类,则可以在Test基类中定义一次。

对于不需要更改所有类的内容,您可以实现Junit自定义套件运行器(请参阅第二个代码示例)

只是为了好玩,使用弃用的Thread.stop方法来解决这个hacky解决方案的问题?

在函数启动每个测试的观察者线程之前,超时后会杀死Junit测试线程。

如果测试完成,清理会杀死观察者线程。

适用于这个小小的演示,不确定这是否适用于生产规模。

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

// You could also make this a base class for test classes
public class TimeoutTest {

    Thread watcherThread ;
    Thread junitTestThread;
    final static int testTimeout = 2000;

    @Before
    public void myInit()
    {
        junitTestThread = Thread.currentThread();
        watcherThread = new Thread()
        {
            @Override 
            public void run()
            {
                    try {
                        Thread.sleep(testTimeout);
                        junitTestThread.stop();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
            }
        };
        watcherThread.start();
    }

    @After
    public void myCleanup() throws InterruptedException
    {
        watcherThread.stop();
    }

    @Test
    public void testPassingFastTest() throws InterruptedException {
        Thread.sleep(1000);
    }

    @Test
    public void testFailingSlowTest() throws InterruptedException {
        Thread.sleep(3000);
    }

}

或者使用套件为几个测试类执行此操作:

import java.util.Arrays;

import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;

@RunWith(AllTests.class)
public class AllTests extends Suite{

    Thread watcherThread ;
    Thread junitTestThread;
    final static int testTimeout = 70;

    public AllTests(final Class<?> clazz) throws InitializationError {
        super(clazz, findClasses());
      }

    private static Class<?>[] findClasses() {
        // You could write code here to get the list of all test classes from specific directories
        return new  Class<?>[] {TimeoutTest.class,TimeoutTest2.class};
      }


    @Override
    public void run(final RunNotifier notifier) {


      notifier.addListener(new RunListener() {
        @Override
        public void testStarted(final Description description) {            
            System.out.println("Before test " + description.getDisplayName());
            junitTestThread = Thread.currentThread();
            watcherThread = new Thread()
            {
                @Override 
                public void run()
                {
                        try {
                            Thread.sleep(testTimeout);
                            junitTestThread.stop();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                }
            };
            watcherThread.start();

        }

        @Override
        public void testFinished(final Description description) {
            System.out.println("After test " + description.getDisplayName());
            watcherThread.stop();

        }

      }
    );

    super.run(notifier);


    }      
}

答案 2 :(得分:0)

可能不是您正在寻找的解决方案,但如果您在连续构建服务器中运行测试,则通常会有一个全局超时的作业。 e.g. for Jenkins

答案 3 :(得分:0)

@Test@Rule注释仅适用于JUnit4。对于JUnit3,您必须手动管理一个新线程来设置超时时间:

public void testWithTimeout() throws InterruptedException, TimeoutException {
    final int factorialOf = 1 + (int) (30000 * Math.random());
    System.out.println("computing " + factorialOf + '!');

    Thread testThread = new Thread() {
        public void run() {
            System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf));
        }
    };

    testThread.start();
    Thread.sleep(1000);
    testThread.interrupt();

    if (testThread.isInterrupted()) {
        throw new TimeoutException("the test took too long to complete");
    }
}

来源和更多信息:https://netbeans.org/kb/docs/java/junit-intro.html#Exercise_24

或更紧凑的解决方案是将CallableFuture一起使用。此处的信息:How can I wrap a method so that I can kill its execution if it exceeds a specified timeout?

相关问题