1. 概述

JUnit 是 Java 中进行单元测试的首选工具。在测试执行过程中,开发者常常会遇到一个奇怪的错误:即使导入了正确的类,却提示没有可运行的方法。

本教程中,我们将探讨导致此错误的几种典型情况,并给出解决方案。

2. 缺少 @Test 注解

首先,测试引擎必须能够识别测试类才能执行测试。如果没有有效的测试方法,就会抛出异常:

java.lang.Exception: No runnable methods

为了避免这种情况,务必确保测试类的方法上使用了 JUnit 库中的 @Test 注解。

对于 JUnit 4.x,应使用:

import org.junit.Test;

而对于 JUnit 5.x,则应从 JUnit Jupiter 导入:

import org.junit.jupiter.api.Test;

此外,要特别注意 TestNG 框架的 @Test 注解:

import org.testng.annotations.Test;

⚠️ 如果错误地导入了 TestNG 的 @Test 注解,就会导致“no runnable methods”错误。

3. 混用 JUnit 4 和 JUnit 5

一些遗留项目可能同时在类路径中包含 JUnit 4 和 JUnit 5 库。虽然编译器不会报错,但在运行 JUnit 测试时可能会遇到“no runnable methods”错误。我们来看几种情况。

3.1. 错误的 JUnit 4 导入

这通常是由于 IDE 的自动导入功能,它导入了第一个匹配的类。正确的 JUnit 4 导入如下:

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

如上所示,org.junit 包包含了 JUnit 4 的核心类。

3.2. 错误的 JUnit 5 导入

同理,我们可能错误地导入了 JUnit 4 的类而不是 JUnit 5,导致测试无法运行。因此,必须确保为 JUnit 5 导入正确的类:

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

这里可以看到,JUnit 5 的核心类属于 org.junit.jupiter.api 包。

3.3. @RunWith 和 @ExtendWith 注解

对于使用 Spring 框架的项目,集成测试需要导入特殊的注解。在 JUnit 4 中,我们使用 @RunWith 注解来加载 Spring TestContext 框架。

然而,对于 JUnit 5 编写的测试,应使用 @ExtendWith 注解来实现相同的功能。 如果在不同版本的 JUnit 中混用这两个注解,测试引擎可能无法找到测试方法。

另外,在 Spring Boot 应用中执行 JUnit 5 测试,可以使用 @SpringBootTest 注解,它在 @ExtendWith 的基础上提供了额外功能。

4. 测试工具类

工具类有助于在不同类中复用代码。同样,测试工具类或父类可以共享一些通用的设置或初始化方法。由于类名符合测试类的命名约定,测试引擎会将它们误认为真实的测试类,并尝试寻找可测试的方法。

看一个工具类的例子:

public class NameUtilTest {
    public String formatName(String name) {
        return (name == null) ? name : name.replace("$", "_");
    }
}

注意,NameUtilTest 类符合真实测试类的命名约定 但它没有使用 @Test 注解的方法,因此会导致“no runnable methods”错误。为了避免这种情况,应重新考虑这些工具类的命名。

✅ 可以将名称以“Test”结尾的工具类重命名为“TestHelper”或类似名称:

public class NameUtilTestHelper {
    public String formatName(String name) {
        return (name == null) ? name : name.replace("$", "_");
    }
}

或者,对于以“Test”结尾的父类(如 BaseTest),可以将其声明为 abstract,以防止测试引擎执行。

5. 被显式忽略的测试

虽然不常见,但有时所有测试方法或整个测试类可能被错误地标记为跳过。

@Ignore(JUnit 4)和 @Disabled(JUnit 5)注解可用于临时跳过某些测试。 当测试修复复杂或需要紧急部署时,这是一个快速恢复构建的临时方案:

public class JUnit4IgnoreUnitTest {
    @Ignore
    @Test
    public void whenMethodIsIgnored_thenTestsDoNotRun() {
        Assert.assertTrue(true);
    }
}

上述例子中,JUnit4IgnoreUnitTest 类只有一个方法,且被标记为 @Ignore。当运行测试(无论是 IDE 还是 Maven 构建)时,由于该测试类没有可执行的方法,可能会导致“no runnable methods”错误。

为了避免这个错误,最好移除 @Ignore 注解,或者至少保留一个有效的测试方法。

6. 总结

本文中,我们探讨了在 JUnit 中运行测试时遇到“no runnable methods”错误的几种情况,并给出了相应的解决方案。

首先,我们了解到缺少正确的 @Test 注解会导致此错误。其次,混用 JUnit 4 和 JUnit 5 的类也会引发同样的问题。我们还讨论了测试工具类的最佳命名方式。最后,我们分析了显式忽略测试可能带来的问题。

一如既往,本文中的代码可以在 GitHub 上获取。


原始标题:Avoiding “no runnable methods” Error in JUnit | Baeldung