Java.lang.Exception:在运行JUnits时没有可运行的方法异常

130

我正在尝试在Linux命令提示符上运行JUnit。/opt/junit/包含必要的JARS(hamcrest-core-1.3.jar和junit.jar)和类文件,我正在使用以下命令来运行JUnit:

java -cp hamcrest-core-1.3.jar:junit.jar:. org.junit.runner.JUnitCore  TestRunner

测试Junit类:

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TestJunit {
   @Test
   public void testAdd() {
      String str= "Junit is working fine";
      assertEquals("Junit is working fine",str);
   }
}

测试运行器:

import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

public class TestRunner {
   public static void main(String[] args) {
      Result result = JUnitCore.runClasses(TestJunit.class);
      for (Failure failure : result.getFailures()) {
         System.out.println("fail ho gaya"+failure.toString());
      }
      System.out.println("passed:"+result.wasSuccessful());
   }
}  
我运行此代码时遇到了以下异常
JUnit version 4.11
.E
Time: 0.003
There was 1 failure:
1) initializationError(TestRunner)
java.lang.Exception: No runnable methods
    at org.junit.runners.BlockJUnit4ClassRunner.validateInstanceMethods(BlockJUnit4ClassRunner.java:169)
    at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:104)
    at org.junit.runners.ParentRunner.validate(ParentRunner.java:355)
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:57)
    at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:10)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runner.Computer.getRunner(Computer.java:40)
    at org.junit.runner.Computer$1.runnerForClass(Computer.java:31)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.runners.model.RunnerBuilder.runners(RunnerBuilder.java:101)
    at org.junit.runners.model.RunnerBuilder.runners(RunnerBuilder.java:87)
    at org.junit.runners.Suite.<init>(Suite.java:80)
    at org.junit.runner.Computer.getSuite(Computer.java:28)
    at org.junit.runner.Request.classes(Request.java:75)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:117)
    at org.junit.runner.JUnitCore.runMain(JUnitCore.java:96)
    at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:47)
    at org.junit.runner.JUnitCore.main(JUnitCore.java:40)

FAILURES!!!
Tests run: 1,  Failures: 1

不知道,正在浏览入门教程。这些文件是从教程本身复制过来的。无论如何,我得到了我的答案,http://sqa.fyicenter.com/FAQ/JUnit/Can_You_Explain_the_Exception_No_runnable_meth.html。 - Vipin Verma
3
可能是 java.lang.exception no runnable methods junit 的重复问题。 - CJBS
1
没有一个答案起作用。结果我在我的一个单元测试中有一行双括号初始化...在JDK 8上...当我删除它时,这个错误就消失了!它是这样的:myObject.setSomething(new OtherObject() {{/Put literally anything here/}}); 找到这个问题花了几个小时(一直以为我的导入会从另一个类中带来一些不良的静态初始化程序,坏的类加载器,反射垃圾等)。我相信这可能是JVM的一个bug,但没有证据,所以我把它留作评论而不是答案。简而言之,“删除双括号初始化的{{}}部分”。 - Jason D
2
我遇到了同样的错误,当我在测试类上移除了“public”访问修饰符时,该错误消失了,并且测试正常运行。目前还没有深入了解原因,但是在这里添加此信息以帮助其他人可能会有所帮助。 - levenshtein
30个回答

164

在我的情况下,我导入了错误的包:

import org.testng.annotations.Test;

代替

import org.junit.Test;

小心你的 IDE 自动补全功能。


51
在我的情况下,应该使用的是import org.junit.jupiter.api.Test; - Master Chief
是的,可能是任何错误的@Test注释导入,将其修复为org.junit.Test可以解决问题。谢谢! - JustRandom
导入 import org.junit.Test; 是表示您正在使用 JUnit 4 吗? - wolf97084
1
谢谢你节省了我的时间!我也有一个TestNG导入。 - Piyush Upadhyay
4
是的,org.junit.Test 对应 JUnit 4,org.junit.jupiter.api.Test 对应 JUnit 5。 - John
它解决了错误还是只是不进行测试? - Gustavo

152

53
我遇到了 Junit5 的错误,我没有任何没有 '@Test' 注解的类。 - Durga Swaroop
3
由于我的@Before方法命名为setUp()而不是setup(),导致了我的问题。 - ben_joseph
我想知道是哪个类引起了问题。我不认为我需要手动检查数百个类,只因为Mockito太害羞而不告诉我。 - Christian Vincenzo Traina

45

我的控制器测试简单明了:

@RunWith(SpringRunner.class)
@SpringBootTest
public class TaskControllerTest {
   //...
   //tests
   //
}

我只是移除了 "public",然后就神奇地工作了。


你指的是哪个“public”? - Jack
我和同样的错误... - Chris Wu
2
这个修复背后的魔力是什么? - gai-jin
1
谢谢。在课前去掉公共,之后一切都正常了。 - magdi amer

22
这个解决方案只适用于极少数人,通常是那些实现自己的JUnit测试运行器并使用单独的ClassLoader的人。当你从不同的ClassLoader加载一个类,然后尝试从从系统ClassLoader加载的JUnitCore实例运行该测试时,就会出现这种情况。例如:
// Load class
URLClassLoader cl = new URLClassLoader(myTestUrls, null);
Class<?>[] testCls = cl.loadClass("com.gubby.MyTest");

// Run test
JUnitCore junit = new JUnitCore();
junit.run(testCls); // Throws java.lang.Exception: No runnable methods

查看堆栈跟踪:

java.lang.Exception: No runnable methods
at org.junit.runners.BlockJUnit4ClassRunner.validateInstanceMethods(BlockJUnit4ClassRunner.java:169)
at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:104)
at org.junit.runners.ParentRunner.validate(ParentRunner.java:355)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:57)
at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:10)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
at org.junit.runner.JUnitCore.run(JUnitCore.java:138)

问题实际上出现在BlockJUnit4ClassRunner:169处(假设为JUnit 4.11):

https://github.com/junit-team/junit/blob/r4.11/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java#L95

它会检查哪些方法被标注了@Test

protected List<FrameworkMethod> computeTestMethods() {
    return getTestClass().getAnnotatedMethods(Test.class);
}

在这种情况下,Test.class将使用系统类加载器加载(即加载JUnitCore的那个),因此从技术上讲,您的任何测试方法都不会带有该注释。解决方案是将JUnitCore与测试本身在同一个类加载器中加载。

编辑: 针对用户user3486675的问题,您需要创建一个不委托给系统类加载器的ClassLoader,例如:

private static final class IsolatedURLClassLoader extends URLClassLoader {
    private IsolatedURLClassLoader(URL[] urls) {
        // Prevent delegation to the system class loader.
        super(urls, null);
    }
}

将包含所需内容的一组URL传递给此函数。您可以通过过滤系统类路径来创建它。请注意,您不能简单地委托给父ClassLoader,因为那些类将由父ClassLoader而不是测试类的ClassLoader加载。
然后,您需要从此ClassLoader加载的类中启动整个JUnit作业。这里变得有点混乱。类似下面这样的东西:
public static final class ClassLoaderIsolatedTestRunner {

    public ClassLoaderIsolatedTestRunner() {
        // Disallow construction at all from wrong ClassLoader
        ensureLoadedInIsolatedClassLoader(this);
    }

    // Do not rename.
    public void run_invokedReflectively(List<String> testClasses) throws BuildException {
        // Make sure we are not accidentally working in the system CL
        ensureLoadedInIsolatedClassLoader(this);

        // Load classes
        Class<?>[] classes = new Class<?>[testClasses.size()];
        for (int i=0; i<testClasses.size(); i++) {
            String test = testClasses.get(i);
            try {
                classes[i] = Class.forName(test);
            } catch (ClassNotFoundException e) {
                String msg = "Unable to find class file for test ["+test+"]. Make sure all " +
                        "tests sources are either included in this test target via a 'src' " +
                        "declaration.";
                throw new BuildException(msg, e);
            }
        }

        // Run
        JUnitCore junit = new JUnitCore();
        ensureLoadedInIsolatedClassLoader(junit);
        junit.addListener(...);
        junit.run(classes);
    }

    private static void ensureLoadedInIsolatedClassLoader(Object o) {
        String objectClassLoader = o.getClass().getClassLoader().getClass().getName();

        // NB: Can't do instanceof here because they are not instances of each other.
        if (!objectClassLoader.equals(IsolatedURLClassLoader.class.getName())) {
            throw new IllegalStateException(String.format(
                    "Instance of %s not loaded by a IsolatedURLClassLoader (loaded by %s)",
                    cls, objectClassLoader));
        }
    }
}

然后,您需要通过反射调用运行程序:

Class<?> runnerClass = isolatedClassLoader.loadClass(ClassLoaderIsolatedTestRunner.class.getName());

// Invoke via reflection (List.class is OK because it just uses the string form of it)
Object runner = runnerClass.newInstance();
Method method = runner.getClass().getMethod("run_invokedReflectively", List.class);
method.invoke(...);

这对我很有帮助。谢谢!一个备注:也可以选择使用系统类加载器作为自定义类加载器的父级。请参阅URLClassLoader的构造函数参数。这样,父类加载器将首先尝试加载类,仅在不可能时,自定义类加载器才会尝试加载类。 - Icarus
你如何在与测试本身相同的ClassLoader中加载JUnitCore? - D Malan
@user3486675,我已经添加了一个最安全的方法示例。它看起来不太美观。 - gub
@gubby 你好,上面的代码无法编译(junit.addListener(...);),而且cls未定义。我们也遇到了这个问题,想知道如何解决它。 - ALM

19

我现在遇到了测试代码相同的问题,这是由于spring boot中的@RunWith注释引起的。我使用了:

@RunWith(SpringRunner.class)

有了这个注释,JUnit Vintage会运行,但找不到任何测试并给出错误。我已经将其删除,只运行JUnit Jupiter,一切都很好。


1
是的,这对我也起作用了 - 我正在使用带有此注释的junit5测试! - kristjan reinhold
也适用于我! - Lime莉茉
1
解释:在JUnit5中,@RunWith被替换为@ExtendWith,而@SpringBootTest注释已经包含了它,因此不需要添加它。 - cdalxndr

18

我不得不修改导入语句:

import org.junit.jupiter.api.Test;

import org.junit.Test;

12

在我的情况下,我使用了错误的 Test 导入。正确的是 import org.junit.Test;


谢谢,我也遇到了同样的问题。 - Haranath Boggarapu
是的,它已经修复了,但为什么?你能解释一下吗? - Siamak

8
如果你使用import org.junit.jupiter.api.Test (Junit 5)并且使用@RunWith(SpringRunner.class),SpringRunner在Junit4上,这会让junit感到困惑。 将类名前的public删除就能工作了,因为Junit5会抱怨public测试类。 从文档来看: JUnit5对于测试类的可见性比JUnit4更具有容错性,JUnit4需要所有内容都是public。 在这个上下文中,JUnit5测试类可以具有任何可见性,但是建议使用默认包可见性,这有助于提高代码的可读性。

7

在我的情况下,我只是禁用了 //@RunWith(SpringRunner.class)。

这样就不会出现异常了。


@SpringBootTest(classes = {ServletWebServerFactoryAutoConfiguration.class}, webEnvironment = RANDOM_PORT, properties = {"spring.cloud.config.enabled=false"}) @ExtendWith(MockitoExtension.class) @AutoConfigureMockMvc - Smart Coder

7

对我来说,将import org.junit.jupiter.api.Test;替换为import org.junit.Test;有所帮助。


这对我真的很有用。谢谢 :) 你能告诉我为什么我们必须使用另一个依赖吗? - Imran Faruqi
@ImranFaruqi org.junit.jupiter.api.Test 来自 JUnit 5,而 org.junit.Test 来自 JUnit 的早期主要版本。 - gouessej

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接