Java singleton - 为类编写单元测试?

时间:2017-09-29 22:45:30

标签: java junit singleton mockito

我有一个java类 - 非常典型的通常单例 - 像这样:

请注意:为了简洁起见,我在这里省略了“如果为空”的逻辑,因为这不是我遇到的麻烦,而且我不想挤满问题。

public class MySingleton
{
    ObjectMapper mapper;

    private MySingleton()
    {
        new MySingleton(new ObjectMapper())
    }

    private MySingleton(ObjectMapper mapper)
    {
        this.mapper = mapper;
    }

    private static final class Lazy
    {
        static final MySingleton INSTANCE = new MySingleton();
    }

    public static MySingleton getInstance()
    {
        return Lazy.INSTANCE;
    }
}

现在 - 这很棒 - 而且确实有效 - 但如果我试图在单元测试中测试它会怎样......

我想嘲笑映射器 - 所以我可以这样做:

ObjectMapper mockObjectMapper = mock(ObjectMapper.class)

但是,当我需要以某种方式调用“MySingleton”的构造函数来测试它时...

我该怎么做 - 鉴于我的测试课,我知道它会说“MySingleton(这里的参数)在MySingleton中有私人访问权限”?

2 个答案:

答案 0 :(得分:0)

单身人士是可测试代码的敌人。 that question答案中给出的链接是关于单身人士的邪恶以及为什么以及如何避免它们的极好论证。

答案 1 :(得分:0)

您可以使用 PowerMock 使用ConstructorMocking注入测试ObjectMapper实例。

http://benkiefer.com/blog/2013/04/23/powermockito-constructor-mocking/

我必须修改你的示例单例,以便构造函数被正确链接。

public class MySingleton {
  ObjectMapper mapper;

  private MySingleton()
   {
      //This does not work.
      //new MySingleton(new ObjectMapper());

      this(new ObjectMapper());
   }

  private MySingleton(ObjectMapper mapper)
   {
     this.mapper = mapper;
   }

  private static final class Lazy
   {
     static final MySingleton INSTANCE = new MySingleton();
   }

  public static MySingleton getInstance()
   {
      return Lazy.INSTANCE;
   }
}

我还创建了ObjectMapper类。

public class ObjectMapper {
    //Empty Sample uses default CTR
}

我可以使用之前列出的链接中的说明进行如下测试:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


@RunWith(PowerMockRunner.class) 
@PrepareForTest(MySingleton.class) 
public class MySingletonTest {


    @Test
    public void testSingletonCtr() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        PowerMockito.whenNew(ObjectMapper.class).withNoArguments().thenReturn(mapper);

        Assert.assertEquals(MySingleton.getInstance().mapper, mapper);
    } 
}

我在maven项目中这样做。我需要将以下依赖项添加到我的测试范围中:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4-rule</artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</dependency>

我确实倾向于同意单身人士往往会长期导致代码维护和可扩展性方面的问题。如果您有能力寻找问题的替代方法,那么这样做可能会对您有所帮助。如果没有,那么我相信 PowerMock 实用程序将为您提供您正在寻找的功能。

最好的运气。