Search code examples
jmockit

How to generalize a JMockit test using Spring autowiring


So I would like to use a generic test for a few different Dao methods. Inside the Dao, I implemented the save functionality to be Entity independent, so I figured it would be best to make the tests Entity independent as well. Currently I have the following for one of my tests that is autowired with .

@Injectable
public EntityManager em;

@Tested
SyncClaimDao syncClaimDao = new SyncClaimDaoImpl();

@Before
public void setUp() {
    Deencapsulation.setField(syncClaimDao, "em", em);
}

private void testSaveEntity (Class T) {
    // Existing claim happy path
    new Expectations() {
        {
            em.contains(any); result = true;
            em.merge(any);
        }
    };

    if (T.isInstance(SyncClaimEntity.class)) {
        Assert.assertTrue(syncClaimDao.saveClaim(new SyncClaimEntity()));
    } else if (...) {...}
}

@Test
public void testSaveClaim() {
    testSaveEntity(SyncClaimEntity.class);
}

SyncClaimDaoImpl

@Override
public boolean saveClaim(SyncClaimEntity claim) {
    return saveEntity(claim);
}

private boolean saveEntity(Object entity) {
    boolean isPersisted = false;

    try {
        isPersisted = em.contains(entity);

        if (isPersisted) {
            em.merge(entity);
        } else {
            em.persist(entity);
            em.flush();
            isPersisted = true;
        }
        logger.debug("Persisting " + entity.getClass().getSimpleName() + ": " + entity.toString());
    }
    catch (NullPointerException ex) {
        ...
    }
    catch (IllegalArgumentException ex) {
        ...
    }

    return isPersisted;
}

When I run the tests I am seeing the following errors:

mockit.internal.MissingInvocation: Missing invocation of:
javax.persistence.EntityManager#contains(Object)
   with arguments: any Object
   on mock instance: javax.persistence.$Impl_EntityManager@44022631
    at at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    ... 4 more 
Caused by: Missing invocation
    at [redacted].dal.dao.SyncClaimDaoImplTest$1.<init>(SyncClaimDaoImplTest.java:48)
    at [redacted].dal.dao.SyncClaimDaoImplTest.testSaveEntity(SyncClaimDaoImplTest.java:46)
    at [redacted].dal.dao.SyncClaimDaoImplTest.testSaveClaim(SyncClaimDaoImplTest.java:67)
    ... 10 more

Now if I just move the Expectations block into the @Test method like so:

@Test
public void testSaveClaim() {

    new Expectations() {
        {
            em.contains(any); result = true;
            em.merge(any);
        }
    };

    Assert.assertTrue(syncClaimDao.saveClaim(new SyncClaimEntity()));

I get a successful test run as should be. I'm thinking that the autowiring for the Test method is not properly scoping my Expectations. That's why I'm seeing the missing invocation errors.

Does anyone have any ideas on how to generalize my Expectations so I can create simpler tests for generalized methods?


Solution

  • I see the mistake now: T.isInstance(SyncClaimEntity.class). The Class#isInstance(Object) method is supposed to be called with an instance of the class, not with the class itself; so, it's always returning false because SyncClaimEntity.class is obviously not an instance of SyncClaimEntity.