使用JUnit在内部类中编写测试用例

87

我读到了为单元测试结构化的文章,建议每个类都有一个测试类,每个方法都有一个内部类。觉得这似乎是一种方便组织测试的方式,于是我在我们的Java项目中尝试了一下。然而,内部类中的测试好像根本没有被检测到。

我大致做了这样的事情:

public class DogTests
{
    public class BarkTests
    {
        @Test
        public void quietBark_IsAtLeastAudible() { }

        @Test
        public void loudBark_ScaresAveragePerson() { }
    }

    public class EatTests
    {
        @Test
        public void normalFood_IsEaten() { }

        @Test
        public void badFood_ThrowsFit() { }
    }
}

JUnit不支持这个吗,还是我做错了?

1
我认为如果你将内部类声明为“静态”,它应该可以工作。 - Stephan
请参见此处:https://dev59.com/b2855IYBdhLWcg3wxHcq - cuh
6个回答

106

你应该使用@RunWith(Enclosed.class)注释你的类,就像其他人说的那样,将内部类声明为静态:

@RunWith(Enclosed.class)
public class DogTests
  {
  public static class BarkTests
  {
    @Test
    public void quietBark_IsAtLeastAudible() { }

    @Test
    public void loudBark_ScaresAveragePerson() { }
  }

  public static class EatTests
  {
    @Test
    public void normalFood_IsEaten() { }

    @Test
    public void badFood_ThrowsFit() { }
  }
}

你仍然只能一次运行一个类。 - Sridhar Sarnobat
3
对我来说没问题。至少在Intellij中,我可以运行所有测试、只运行一个子类的测试或单个测试。有了代码折叠功能,这真的很方便! - Willey
使用Eclipse Neon和JUnit 4.12为我运行所有测试。Eclipse还提示运行外部fixture或其中一个嵌套fixture。唯一的问题是,当从Gradle运行时,来自嵌套fixture的测试会运行两次。[GRADLE-2843](https://issues.gradle.org/browse/GRADLE-2843)于2013年针对此问题进行了开放,但此后已被放弃。 - Rusty Shackleford
同时请注意内部类必须是公共的。 - tkruse
这似乎已经过时了 - 请参见https://dev59.com/T2oy5IYBdhLWcg3wHqiB#22546453 - Matthew

32
public class ServicesTest extends TestBase {

   public static class TestLogon{

       @Test
       public void testLogonRequest() throws Exception {
         //My Test Code
       }
   }
}

将内部类声明为静态的对我有用。


2
你仍然只能同时运行1个类。 - Sridhar Sarnobat
1
@Sridhar-Sarnobat 你在说什么?至少在JUnit 4.12中可以工作。不过我懒得测试旧版本。 - Adowrath
另外,如果使用Gradle,执行./gradlew clean对我来说似乎是有效的(我想)。 - Dylanthepiguy

26

在JUnit 5中,你只需要将非静态内部类标记为 @Nested

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class DogTests {
    @Nested
    public class BarkTests {
        @Test
        public void quietBark_IsAtLeastAudible() { }

        @Test
        public void loudBark_ScaresAveragePerson() { }
    }

    @Nested
    public class EatTests {
        @Test
        public void normalFood_IsEaten() { }

        @Test
        public void badFood_ThrowsFit() { }
    }
}

1
这是截至2022年最新的答案。 同时,这也是绕过Spring的@TestPropertySource只能应用于类的烦人事实的最佳方法。 - Miguel Ruiz

15

我认为一些答案可能是针对旧版本的JUnit。在JUnit 4中,这对我起作用:

    @RunWith(Suite.class)
    @Suite.SuiteClasses({ DogTests.BarkTests.class, DogTests.EatTests.class })
    public class DogTests
    {
        public static class BarkTests
        {
            @Test
            public void quietBark_IsAtLeastAudible() { }

            @Test
            public void loudBark_ScaresAveragePerson() { }
        }

        public static class EatTests
        {
            @Test
            public void normalFood_IsEaten() { }

            @Test
            public void badFood_ThrowsFit() { }
        }
    }

10

我也使用过Nitor Creation的嵌套运行器,并且取得了成功。

Nitor Creation的嵌套运行器如何使用

这里有篇解释它的文章

添加这个依赖项:

<dependency>
    <groupId>com.nitorcreations</groupId>
    <artifactId>junit-runners</artifactId>
    <version>1.2</version>
    <scope>test</scope>
</dependency>

并在您的测试中添加@RunWith

import com.nitorcreations.junit.runners.NestedRunner
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
  
@RunWith(NestedRunner.class)
public class RepositoryUserServiceTest {
           
    public class RegisterNewUserAccount {
     
        public class WhenUserUsesSocialSignIn {
             
            public class WhenUserAccountIsFoundWithEmailAddress {
                 
                @Test
                public void shouldThrowException() {
                     assertTrue(true);
                }
            }
         
        }
    }
}

PS:示例代码已从上述博客文章中提取并修改。


3
我正在寻找一种在JUnit中显示类似规范的东西的方法......太棒了!谢谢! - Tony K.
这个也适用于 Kotlin(在这种情况下使用@RunWith(NestedRunner::class))。 - x1a4

0

我刚看到这篇关于内部类测试的帖子(11年后)。如果一个内部类本来就应该是静态的,那么它可以轻松地转换为等效的静态形式。静态内部类并不是真正的内部类,因为没有封装的this。除了可见性限制外,它们具有完全相同的语义,与顶级类相同。

要测试“真正”的内部类[依赖于其封闭实例的内部类],您需要使用Java语言提供的用于在封闭类范围之外创建内部类实例的接口。每个构造函数中都包含一个额外的参数,即封闭实例。通过这种方式,Java编译器将内部类转换为具有修改名称(大量$符号)和增强构造函数的特殊顶级类。相同的转换可以在源级别执行。原则上,这些转换后的类可以进行测试,但这是一个复杂的过程,因为被测试程序具有转换后的语法,测试代码必须构造一个(模拟)对象作为封闭实例。

另一种测试真正内部类的方法是为每个方法编写可执行的方法契约,其中包括可执行的逻辑前置条件和可执行的逻辑后置条件。然后,在调用内部类方法的传统顶级测试运行过程中,可以评估这些可执行合同。

实际上,在进行顶层测试的过程中,我通常会采用间接测试内部类方法。编写和测试所有方法的可执行合同是一种更严格但显著更昂贵的替代方案。

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