简体   繁体   中英

JUnit/Mockito Test case gets passed in Debug Mode but not in Run mode

I am using following method to test the Profile Object updation in my project.

import org.apache.commons.collections.MapUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;

import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.Callable;


@RunWith(MockitoJUnitRunner.class)
@PrepareForTest({TxUtils.class})
public class MonitoringProfileServicesImplTest extends MonitoringProfileServicesImpl {

    private MutableDataObject mutableDataObject = Mockito.mock(MutableDataObject.class);

    @Mock
    private PersistenceContext<MutableDataObject> persistenceContext;

    @Mock
    PersistenceContextHelper persistenceContextHelper;

    @Mock
    MonitoringDao monitoringDao;

    @InjectMocks
    private MonitoringProfileServicesImpl service;

    private BigInteger profileId;

    private Map<String, BigInteger> attributeNameIdMap;

    private Map<BigInteger, String> invAttributeNameIdMap;

    ResponseBean responseBean = new ResponseBean();


    @Before
    public void setUp() {
        Mockito.when(persistenceContextHelper.getLocalPersistenceContext()).thenReturn(persistenceContext);

        profileId = BigInteger.valueOf(Long.valueOf(1L));

        attributeNameIdMap = new HashMap<String, BigInteger>();
        invAttributeNameIdMap = new HashMap<BigInteger, String>();
        attributeNameIdMap.put("key", BigInteger.valueOf(1111L));
        invAttributeNameIdMap = MapUtils.invertMap(attributeNameIdMap);
    }


 @Test

public void testXXXX() {
        MonitoringProfile monitoringProfile =  getDummyMonitoringProfileToBeCreated();

    TransactionServicePALImpl transactionImpl = Mockito.mock(TransactionServicePALImpl.class);
    Mockito.when(transactionImpl.getTM()).thenReturn(new TransactionManagerImplTest());


    Mockito.when(mutableDataObject.getId()).thenReturn(BigInteger.valueOf(1000));
    Mockito.when(mutableDataObject.getName()).thenReturn("Name");

    Mockito.when(persistenceContext.getObjectById(Mockito.any(BigInteger.class))).thenReturn(mutableDataObject);
    Mockito.when(monitoringDao.getAttributeIdsForObjectType(Mockito.any(BigInteger.class))).thenReturn(invAttributeNameIdMap);

    TxUtils mock = PowerMockito.mock(TxUtils.class);
    System.out.println("newresponseBean:::");



        PowerMockito.when(mock.doTxRequired(new Callable<Object>() {
            @Override
            public Object call() throws Exception {

                return this;
            }

        })).thenAnswer(new Answer<ResponseBean>() {
            @Override
            public ResponseBean answer(InvocationOnMock invocation) throws Throwable {

                responseBean.setResultObj(monitoringProfile);
                return responseBean;
            }
        });


    ResponseBean newresponseBean = service.updateMonitoringProfile(monitoringProfile, BigInteger.valueOf(1000));
    Assert.assertNotNull(newresponseBean);
    Assert.assertNotNull(newresponseBean.getResultObj());
    Assert.assertNull(newresponseBean.getErrorObj());
}

    }   

**In run mode it is getting failed on following line

PowerMockito.when(mock.doTxRequired(new Callable<Object>().....

While in debug mode it is working fine.**

Following is service method code;

public ResponseBean updateMonitoringProfile(@RequestBody MonitoringProfile profileToUpdate) {         
        ResponseBean responseBean = TxUtils.doTxRequired(() -> {
            ResponseBean response = new ResponseBean();
            try {
                PersistenceContext<MutableDataObject> localPersistenceContext = persistenceContextHelper.getLocalPersistenceContext();
                MutableDataObject monitoringProfile = localPersistenceContext.getObjectById(profileToUpdate.getId());
                ParamsBuilder paramsBuilder = populateProfileParameterValues(profileToUpdate, monitoringProfile).build();
                localPersistenceContext.flush();
                response.setResultObj(profileToUpdate);
            } catch (NotUpdatedException notUpdatedEx) {
                notUpdatedEx.printStackTrace();
            }
            return response;
        });
        return responseBean;
    }

Following exception we are getting while running in Run mode;

newresponseBean:::

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 org.powermock.api.mockito.PowerMockito.when(PowerMockito.java:495) 
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:483)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
 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:325)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
 at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
 at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
 at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
 at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
 at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
 at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:483)
 at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)


Process finished with exit code -1

If your observation is really true and you do not run into this problem when stepping through the test case in "debugging" mode; but it is solid when running the test normally, then it must be some sort of timing issue.

I guess the point that you have synchronized statement within your test code is an indication that some code you are not showing here will be using multiple threads?!

So given the fact that we will not be able to repro this issue; that is the part I would advise you to look into.

Beyond that: you are getting the "mocking specification" part wrong . You want to instruct your environment that when the static method mock.doTxRequired() is called with some argument, then this or that should happen.

In other words: there is no point in providing a newly created object in your when() clause. To the contrary: the mocking framework will check at runtime if the argument passed at runtime is equal to the object that you specified in your when() clause! And as you are not implementing equals in your anonymous inner class ... PowerMock will never determine that the mock was called with that Object.

In other words: you probably want to use either some object that is equal to what will be passed at runtime; or you use an argument matcher like "any()" or so (see here for details).

And just for the record: if you have the possibility consider to not use static method calls and avoid using PowerMock.

I had same issue. It fails randomly when running tests but always passes when debugging them.

The problem was that my test class was inherited from a base test class and i mocked same service in both of them. after removing the mock in child class, everything fixed

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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