Java 在参数上调用方法时,PowerMockito 模拟静态方法失败

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31587770/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:17:23  来源:igfitidea点击:

PowerMockito mocking static method fails when calling method on parameter

javaunit-testingpowermockito

提问by maccaroo

I'm trying to test a class which uses a calculator class with a number of static methods. I've successfully mocked another class in a similar way, but this one is proving more stubborn.

我正在尝试测试一个使用带有许多静态方法的计算器类的类。我已经以类似的方式成功地嘲笑了另一个班级,但事实证明这个班级更顽固。

It seems that if the mocked method contains a method call on one of the passed in arguments the static method is not mocked (and the test breaks). Removing the internal call is clearly not an option. Is there something obvious I'm missing here?

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

Here's a condensed version which behaves the same way...

这是一个浓缩版本,其行为方式相同......

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;
    }
}

And here's the test...

这是测试...

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(""));
    }
}

It seems there's some confusion about the question, so I've contrived a more 'realistic' example. This one adds a level of indirection, so that it doesn't appear that I'm testing the mocked method directly. The SmallCalculator class is unchanged:

这个问题似乎有些混乱,所以我设计了一个更“现实”的例子。这个增加了一个间接级别,因此看起来我没有直接测试模拟方法。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();
    }
}

And here's the new test class...

这是新的测试课程......

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());
    }
}

采纳答案by maccaroo

I've found the answer here https://blog.codecentric.de/en/2011/11/testing-and-mocking-of-static-methods-in-java/

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

Here's the final code which works. I've tested this approach in the original code (as well as the contrived example) and it works great. Simples...

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

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());
    }
}

回答by vtor

Use anyString()instead of any(String.class).

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

When using any(String.class)the passed argument is null, as Mockito will return the default value for the reference type, which is null. As a result you get an exception.

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

When using the anyString(), the passed argument will be empty string.

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

Note, this explains why you get an exception, however you need to review the way you test your method, as explained in other comments and answers.

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

回答by JFPicard

First of all, remove that line:

首先,删除该行:

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

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

Since you are testing the same method. You don't want to mock the method that you're testing.

由于您正在测试相同的方法。您不想模拟您正在测试的方法。

Secondly, modify your annotation to:

其次,将您的注释修改为:

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

Lastly, add the mock for the length(); like this:

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

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

I think it will do ;)

我认为它会做;)

回答by talex

If you do not want that actual method being invoked. Instead of

如果您不希望调用该实际方法。代替

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

use

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

It is mocking magic and it is difficult to explain why it is actually works.

这是嘲弄魔法,很难解释为什么它实际上有效。

Other thing you forgot is mockStatic(SmallCalculator.class)

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

And you do not need PowerMockito.spy(SmallCalculator.class);

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