模拟 - 缺少方法调用异常

时间:2018-04-29 20:21:49

标签: java junit mocking

我有一个简单的java程序,它有一个服务类,用euros-BitCoinValueService.java检索BitCoin的当前价格。 BitCoinPricer类使用BitCoinValueService类提供的数字并将其转换为美元。

目标是模拟BitCoinValueService类中的对象,因为此数字会频繁波动。

以下是测试类代码:

@RunWith(JUnitParamsRunner.class)
public class BitcoinPricerTest {

    @Mock
    BitCoinValueService bsp; 

    @Before
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testMock (){
        BitCoinPricer bp = new BitCoinPricer(bsp);
        Mockito.when(bsp.findPrice()).thenReturn(6000.00);

        assertEquals(bp.convertEuro(bsp.findPrice()),6000.00,1.0);
        Mockito.verify(bsp).findPrice();
    }
 }

堆栈跟踪:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
   Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
at com.bitcoin.BitcoinPricerTest.testMock(BitcoinPricerTest.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at junitparams.JUnitParamsRunner.runChild(JUnitParamsRunner.java:416)
at junitparams.JUnitParamsRunner.runChild(JUnitParamsRunner.java:385)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

BitCoinPricer类的代码

package com.bitcoin;

public class BitCoinPricer {

    static BitCoinValueService b = new BitCoinValueService();    

    public BitCoinPricer(BitCoinValueService b){
        this.b = b; 
    }

    public static  double convertEuro (double result){

        double euroVal = 1.227481; 

        result = b.findPrice() * euroVal;

        return result;

    }

}

BitCoinValueService的伪代码:

findprice(){
    double result; 
    // do this

    return double result; 
}

我尝试使用像这样的数据提供者来接近它:

@DataProvider
public List<Object[]> dp(){
    final List<Object[]> result = new ArrayList<>();
    result.add( new Object[] {6000.00} );
    return result; 
}

@Test 
@UseDataProvider("dp")
public void testMock (double expectedVal) throws Exception{

    when(mockService.findPrice()).thenReturn(expectedVal);

    // Instantiate the object
    bcp = new BitCoinPricer (mockService);


    //Test begins 
    bcp.convertEuro(0);

    //checking condition
    assertEquals(bcp.convertEuro(expectedVal), 6000.00,0.1);

    verify(mockService, times(1)).findPrice();
}

但是,我得到的参数太多了。

为什么我一直在调用这个误用方法?我将价格设定为6000.00。所以,我可以围绕它构建我的Junit测试。非常感谢你的帮助!

2 个答案:

答案 0 :(得分:2)

除非这是原始问题中的拼写错误,否则需要重构被测系统。

public class BitCoinPricer {
    BitCoinValueService valueService;

    public BitCoinPricer(BitCoinValueService valueService){
        this.valueService = valueService; 
    }

    public double convertEuro (){
        double euroVal = 1.227481; 
        double result = valueService.findPrice() * euroVal;
        return result;
    }
}

然后测试看起来像

@RunWith(JUnitParamsRunner.class)
public class BitcoinPricerTest {
    @Before
    public void initMocks(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testMock (){
        //Arrange
        double price = 6000.00;
        double expected = price * 1.227481;
        BitCoinValueService bsp = Mockito.mock(BitCoinValueService.class);
        Mockito.when(bsp.findPrice()).thenReturn(price);
        BitCoinPricer bp = new BitCoinPricer(bsp);

        //Act
        double actual = bp.convertEuro();

        //Assert
        assertEquals(expected, actual, 1.0);
        Mockito.verify(bsp).findPrice();
    }
}

模拟被安排在调用时按预期运行,并且可以进行断言以验证被测方法是否按预期运行。

答案 1 :(得分:1)

在您的情况下,最好只使用@BeforeClassMockito.mock(BitCoinValueService.class)中创建模拟,然后在@After重置模拟。我认为你的模拟没有正确启动。