简体   繁体   English

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

[英]PowerMockito mocking static method fails when calling method on parameter

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). 似乎如果mocked方法在传入的参数之一上包含方法调用,则静态方法不会被模拟(并且测试中断)。 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());
    }
}

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

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 . 当使用any(String.class) ,传递的参数为null ,因为Mockito将返回引用类型的默认值,即null As a result you get an exception. 结果你得到一个例外。

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. 请注意,这解释了为什么会出现异常,但是您需要检查测试方法的方式,如其他注释和答案中所述。

First of all, remove that line: 首先,删除该行:

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(); 最后,为length()添加模拟; like this: 像这样:

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

I think it will do ;) 我认为它会做;)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM