【问题标题】:How to test singletons in one test class?如何在一个测试类中测试单例?
【发布时间】:2013-09-20 13:19:13
【问题描述】:

我想用以下方法测试单例类的行为:

public class SomeSingleton
{
    private final static int DEFAULT_VALUE1 = ...;
    private final static int DEFAULT_VALUE2 = ...;

    private static SomeSingleton instance;

    public static void init(int value1, int value2)
    {
        if (instance != null)
        {
            throw new IllegalStateException("SomeSingleton already initialized");
        }

        instance = new SomeSingleton(value1, value2);
    }

    public static getInstance()
    {
        if (instance == null)
        {
            init(DEFAULT_VALUE1, DEFAULT_VALUE2);
        }

        return instance;
    }
}

然后我有一个包含几个测试方法的测试类,它会多次调用init

@RunWith(PowerMockRunner.class)
@PrepareForTest(SomeSingleton.class)
public class SomeSingletonTest {
    @Test
    public void testGetInstanceSunnyDay()
    {
        [...]
        SomeSingleton.init(...);
        [...]
        SomeSingleton.getInstance();
        [...]
    }

    @Test
    public void testGetInstanceRainyDay()
    {
        [...]
        SomeSingleton.init(...); // IllegalStateException
        [...]
        SomeSingleton.getInstance();
        [...]
    }
}

当我这样做时,我总是在第二次测试中得到IllegalStateException,因为instance != null

如何在一个测试类中运行涉及init 的多个测试?

testGetInstanceSunnyDaytestGetInstanceRainyDay 放在2 个单独的类中可以解决问题,但我想知道是否有更好的解决方案。

【问题讨论】:

  • 你认为单例在这里提供了什么好处,比如说,一个不可变的类不会?
  • 单例是旧代码库的一部分。我没写。现在,我只想在单元测试中捕捉遗留代码的行为。然后逐步改进应用程序的设计。

标签: java unit-testing junit singleton


【解决方案1】:

基本上单例很难测试,正是因为这种事情。您可以添加一个clearStateForTesting 方法:

static void clearStateForTesting() {
    instance = null;
}

...但我建议如果可能的话,你首先要避免使用单例模式。

还请注意,您的单例实现目前不是线程安全的。如果您真的需要使用单例,则有明显更好的实现。

【讨论】:

  • 另一个类似的选项是使用反射将instance 字段设置为null
  • @JohnB:确实 - 我尽量避免在测试中使用反射。
  • 同意。在使用脆弱的反射和向类中添加仅用于测试原因的方法之间进行糟糕的权衡。无论哪种方式都不是很好。干杯。
  • @JohnB 从我的主观角度来看,反射是这种情况下的最佳选择。请提交您的评论作为答案,我会接受。
  • @DmitriPisarenko:如果您更改私有字段名称,您是否希望您的测试中断?这对我来说听起来非常脆弱。但是,嘿,我会尽量避免从单例开始 :) 请注意,“仅用于测试”方法的存在使得任何阅读代码的人都非常清楚只是测试所需的内容代码。如果其他代码使用单例需要测试不同的配置,它也将更具可重用性。
【解决方案2】:

虽然我同意 Jon 的观点,但另一个选择是使用 ReflectionTestUtils 或一般反射将 instance 字段设置为 null。知道如果字段名称更改,这可能会很脆弱。

【讨论】:

    【解决方案3】:

    鉴于它是单例,init 方法作为公共方法毫无意义。将其设为 private 并在单元测试中仅使用 getInstance

    【讨论】:

      猜你喜欢
      • 2021-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 2012-04-06
      相关资源
      最近更新 更多