Mock a JPA CriteriaBuilder with Mockito

18,665

It turns out you can return mocks from other mocks - so long as you set the right arguments! I was attempting to assert:

when(entityManager.createQuery(anyString())).thenReturn(fakeCriteriaQuery);

When what I actually wanted was to pass in a Class:

when(fakeCriteriaBuilder.createQuery(String.class)).thenReturn(fakeCriteriaQuery);

The error was Mockito's cryptic way of telling me I had screwed up my expectation.

However, I may look into rewriting this test rather than translating what was originally written. As some have pointed out; it's often better to avoid mocking libraries in this way, and the condition being checked for is rather vague.

Share:
18,665
seanhodges
Author by

seanhodges

A senior software developer with an extensive background in Web and mobile technology. Has a passion for creative and modern product development, working with small to medium-sized teams in an Agile environment. Practical experience in project and team management.

Updated on June 30, 2022

Comments

  • seanhodges
    seanhodges almost 2 years

    I have a particularly nasty JMock checking() block for a JPA query that I want to migrate to Mockito:

    Mockery jMock = new Mockery();
    final EntityManager fakeEntityManager = jMock.mock(EntityManager.class);
    final CriteriaBuilder fakeCriteriaBuilder = jMock.mock(CriteriaBuilder.class);
    final CriteriaQuery<String> fakeCriteriaQuery = jMock.mock(CriteriaQuery.class);
    jMock.checking(new Expectations() {{
        oneOf(fakeEntityManager).getCriteriaBuilder(); will(returnValue(fakeCriteriaBuilder));
        oneOf(fakeCriteriaBuilder).createQuery(String.class); will(returnValue(fakeCriteriaQuery));
        oneOf(fakeCriteriaQuery).from(Archiveusergrouplicences.class);
        oneOf(fakeCriteriaQuery).select(with(any(Selection.class)));
        oneOf(fakeCriteriaBuilder).isNotNull(with(any(Expression.class)));
        oneOf(fakeCriteriaQuery).where(with(any(Expression.class)));
        oneOf(fakeEntityManager).createQuery(fakeCriteriaQuery);
        // Return an empty resultset
    }});
    

    The code being tested looks like this:

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<String> criteria = builder.createQuery(String.class);
    
    Root<Archiveusergrouplicences> institution = criteria.from(Archiveusergrouplicences.class);
    criteria.select(institution.get(Archiveusergrouplicences_.usergroupid));    
    criteria.where(builder.isNotNull(institution.get(Archiveusergrouplicences_.usergroupid)));
    
    List<String> result = entityManager.createQuery(criteria).getResultList();
    

    I've found this question on mocking builders, which goes some way to solving the CriteriaBuilder part of the mock; but my main problem is with using mocked objects as the .thenReturn() value of another mock - Mockito does not seem to allow that. For example, for the line:

    CriteriaQuery<String> criteria = builder.createQuery(String.class);
    

    I want to return the mock CriteriaQuery object, like this:

    CriteriaQuery<String> fakeCriteriaQuery = mock(CriteriaQuery.class, RETURNS_DEEP_STUBS);
    when(entityManager.createQuery(anyString())).thenReturn(fakeCriteriaQuery);
    

    This throws a syntax error:

    The method thenReturn(Query) in the type OngoingStubbing is not applicable for the arguments (CriteriaQuery)

    How might I go about testing this code, or improving it to make it more testable?