【问题标题】:Unit Testing a static method单元测试静态方法
【发布时间】:2019-05-11 05:54:39
【问题描述】:

我正在尝试在这里为方法解密编写一个测试用例。

    private static Codec codec;

    static {
        try {
            codec = new Codec(encryptionType, encryptionKey, false, true, false);
        } catch (CodecException e) {
            throw new RuntimeException("Codec initialisation failed", e);
        }
    }


    public static String decrypt(final String toDecrypt) throws CodecException {
        String decrypted = codec.decryptFromBase64(toDecrypt);
        if (decrypted.endsWith(":")) {
            decrypted = decrypted.substring(0, decrypted.length() - 1);
        }
        return decrypted;
    }

测试用例:

    @Mock
    private Codec codec;
    @Test
    public void test_decrypt_Success() throws CodecException {
        when(codec.decryptFromBase64(TestConstants.toDecrypt)).thenReturn(TestConstants.decrypted);
        assertEquals(DocumentUtils.decrypt(TestConstants.toDecrypt), TestConstants.decrypted);
    }

由于这是一个静态方法,我无法在测试套件中注入该类的实例并模拟其编解码器。上述代码按预期从编解码器库中的 assert 抛出错误。

您用什么方法来测试这样的静态方法?还是我根本不应该为此编写测试?

【问题讨论】:

  • 使用 PowerMock 模拟静态方法。但是你甚至没有在你的 Junit 中调用 decrypt(final String toDecrypt) 方法。没有那个,语句 when(codec.decryptFromBase64(TestConstants.toDecrypt)).thenReturn(TestConstants.decrypted);没有意义。
  • @AnkurChrungoo 我的错。更新了q。我的意思是在断言中调用 decrpyt。
  • @talex,我不是要模拟构造函数或超级构造函数。我想在静态方法中模拟在静态块中初始化的静态变量。

标签: java unit-testing testing static mockito


【解决方案1】:

根据我在这些情况下的经验,我只是在 @Before 方法中准备所有实例:

private Codec codec;

@Before
public void setup() throws CodecException {
  codec = new Codec(encryptionType, encryptionKey, false, true, false);
}

【讨论】:

    【解决方案2】:

    有许多不同的捷径可以实现相同的目标(如 cmets 和其他答案中所指出的那样),但从长远来看,并非所有这些方法都是好的。

    我建议创建一个实现解密功能的单例类。所以,你不必创建多个实例,也不需要有静态方法来解密,你可以更容易地注入你的编解码器(我假设你没有多种类型的编解码器根据您的 cmets。但是,如果您这样做,则应相应地调整功能)。

    更多参考:Why use a singleton instead of static methods?

    关于为什么要谨慎使用静态的参考:- Why are static variables considered evil?

    【讨论】:

    • 同意主要思想,但在 Java 中,我更喜欢 IOC,而不是使用静态 getInstance() 方法的旧单例方式。
    • @davidxxx,是的,但很多时候这并不适合我们,尽管从长远来看这是最理想的方式。
    【解决方案3】:

    在 Java 中,静态方法并非旨在设置依赖关系。
    所以把依赖切换成 mock 真的很不自然。
    您可以为该字段提供一个 static 设置器,例如:

    private static Codec codec;
    public static void setCodec(Codec codec){
       this.codec = codec;
    }
    

    你可以用 setCodec(...) 设置一个模拟但是呃......

    但是忘了,只要把事情做好:重构代码以删除所有静态并引入设置编解码器的构造函数。

    private Codec codec;
    public MyClassUnderTest(Codec codec){
       this.codec codec;
    }
    

    IOC 可以在这里帮助使被测类成为单例并简化依赖注入。
    如果在您的情况下不可能,Java 5 枚举至少可以帮助您解决单例问题。

    【讨论】:

    • 我没有使用静态提早器。但是一位同行指出这是一个实用程序类,因此我应该只初始化一次编解码器。我们如何为 util 静态类编写 mock?
    • @tanvi 我也怀疑过!您有一些选择,例如编写适当的面向对象代码并将此代码移动到一个单独的类中,该类执行此特定工作且非静态......或编写静态设置器并相应地调整代码......或使用 PowerMockito模拟静态方法:) 请参阅 blog.codecentric.de/en/2011/11/… 获取 PowerMockito 示例。
    • 我同意单个初始化,但我不同意静态使用。 Ok 在 Java 中的静态方法很容易编写并简化了客户端对 util 方法的使用。但它也有一些限制:这会硬编码依赖关系,因此不能自然切换。我通常只为不需要模拟任何东西的方法编写静态方法。关于您的问题,二传手是一种方式。 PowerMock 是另一个。但在你的情况下,我会避免这两种情况,因为你是 api/组件开发人员。
    • @davidxxx,是的,我宁愿建议将此代码分离到一个单例类中,因为它具有编解码器依赖性。这将使测试/模拟它变得更容易。
    • 如果您不想改变主意,请继续我最初的评论:powermock 或静态设置器。
    猜你喜欢
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2022-01-02
    • 2018-08-23
    • 2019-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多