【问题标题】:PowerMockito mocking static method fails when calling method on parameter在参数上调用方法时,PowerMockito 模拟静态方法失败
【发布时间】:2015-07-23 12:43:58
【问题描述】:

我正在尝试测试一个使用计算器类和一些静态方法的类。我已经以类似的方式成功地模拟了另一个班级,但事实证明这个班级更加顽固。

似乎如果模拟方法包含对传入参数之一的方法调用,则静态方法不会被模拟(并且测试中断)。删除内部调用显然不是一种选择。我这里有什么明显的遗漏吗?

这是一个精简版本,其行为方式相同......

public class SmallCalculator {

    public static int getLength(String string){

        int length = 0;

        //length = string.length(); // Uncomment this line and the mocking no longer works... 

        return length;
    }
}

这是测试...

import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;

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;

import com.solveit.aps.transport.model.impl.SmallCalculator;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SmallCalculator.class})
public class SmallTester {

    @Test
    public void smallTest(){

        PowerMockito.spy(SmallCalculator.class);
        given(SmallCalculator.getLength(any(String.class))).willReturn(5);

        assertEquals(5, SmallCalculator.getLength(""));
    }
}

这个问题似乎有些混乱,所以我设计了一个更“现实”的例子。这增加了一层间接性,因此看起来我没有直接测试模拟方法。 SmallCalculator 类没有改​​变:

public class BigCalculator {

    public int getLength(){

        int length  = SmallCalculator.getLength("random string");

        // ... other logic

        return length;
    }

    public static void main(String... args){

        new BigCalculator();
    }
}

这是新的测试类...

import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;

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;

import com.solveit.aps.transport.model.impl.BigCalculator;
import com.solveit.aps.transport.model.impl.SmallCalculator;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SmallCalculator.class})
public class BigTester {

    @Test
    public void bigTest(){

        PowerMockito.spy(SmallCalculator.class);
        given(SmallCalculator.getLength(any(String.class))).willReturn(5);

        BigCalculator bigCalculator = new BigCalculator();
        assertEquals(5, bigCalculator.getLength());
    }
}

【问题讨论】:

  • 除非这段代码/测试只是为了证明模拟框架无法模拟它应该模拟的东西的概念,否则这里会有很大的混乱。应该模拟正在测试的类,而是模拟它的依赖项,以便她可以测试类的行为。
  • 在这个例子中,当 string.length() 被调用时,我得到一个 NPE。这很奇怪,因为似乎参数被替换了,但实际方法并没有被嘲笑。
  • @Alp 如前所述,原始测试试图模拟一个从实际测试类中调用的类。这个例子只是为了显示失败。

标签: java unit-testing powermockito


【解决方案1】:

我在这里找到了答案https://blog.codecentric.de/en/2011/11/testing-and-mocking-of-static-methods-in-java/

这是最终的有效代码。我已经在原始代码(以及人为的示例)中测试了这种方法,并且效果很好。简单...

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;

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({ SmallCalculator.class})
public class BigTester {

    @Test
    public void bigTest(){

        PowerMockito.mockStatic(SmallCalculator.class);
        PowerMockito.when(SmallCalculator.getLength(any(String.class))).thenReturn(5);

        BigCalculator bigCalculator = new BigCalculator();
        assertEquals(5, bigCalculator.getLength());
    }
}

【讨论】:

  • PowerMockito.when 对我来说是关键。
【解决方案2】:

首先,删除该行:

given(SmallCalculator.getLength(any(String.class))).willReturn(5);

因为您正在测试相同的方法。您不想模拟您正在测试的方法。

其次,将注释修改为:

@PrepareForTest({ SmallCalculator.class, String.class})

最后,为 length() 添加模拟;像这样:

given(String.length()).willReturn(5);

我认为它会做;)

【讨论】:

  • 我没有测试与我模拟的方法相同的方法。我更新了示例以更好地说明这一点。
【解决方案3】:

使用anyString() 代替any(String.class)

当使用any(String.class) 时,传递的参数是null,因为Mockito 将返回引用类型的默认值,即null。结果你得到一个异常。

使用anyString()时,传入的参数为空字符串。

注意,这解释了为什么会出现异常,但是您需要查看测试方法的方式,如其他 cmets 和答案中所述。

【讨论】:

  • 事实上,mocked 方法的内部工作永远不应该被考虑,所以字符串参数的值应该无关紧要。
【解决方案4】:

如果您不希望调用该实际方法。而不是

when(myMethodcall()).thenReturn(myResult);

使用

doReturn(myResult).when(myMethodCall());

这是一种嘲弄魔法,很难解释为什么它真的有效。

你忘记的另一件事是mockStatic(SmallCalculator.class)

你不需要PowerMockito.spy(SmallCalculator.class);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多