Published 02 Oct, 2022

Java - Mockito: multiple calls to mocked UriBuilder keep appending to original URI

Category Java
Modified : Nov 29, 2022
17

I am currently writing some unit tests for legacy code in a rest api application. One particular api call generates a list of URIs using the UriInfo api from Jax-RS. When I mock this interface, I am able to get the tests to run, but each call to the interface appears to reuse the same URI object and appends to it over and over, rather then a new URI object being passed to the tested code. I have a sample class with test that demonstates the issue:

import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

@Stateless
@Path("/")
public class MockitoUrl {

    @PersistenceContext(name = "MockitoTestPU")
    private EntityManager em;
    @Context
    private UriInfo uriInfo;

    @Path("system")
    @GET
    @Produces("text")
    public List<URI> generateUris() {
        List<URI> uris = new LinkedList<>();
        for (int i=1;i<5;i++) {
            final URI uri = uriInfo.getBaseUriBuilder().path(
                    MockitoUrl.class, "generateUris").build("test");
            System.out.println(uri.toString());
        }
        return uris;
    }

}

The unit test:

import java.net.URI;
import java.util.List;
import javax.persistence.EntityManager;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
import org.mockito.MockitoAnnotations;

public class MockitoUrlTest {

    @Mock
    private EntityManager em;
    @Mock
    private UriInfo uri;
    @InjectMocks
    MockitoUrl mockitoUrl;

    public MockitoUrlTest() {
    }

    @Before
    public void setUp() {
        mockitoUrl = new MockitoUrl();
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGenerateUris() throws Exception {
        System.out.println("generateUris");
        String baseUri = "http://server1/api";

        when(uri.getBaseUriBuilder()).thenReturn(UriBuilder.fromUri(baseUri));

        List<URI>result = mockitoUrl.generateUris();

        for (URI u : result) {
            assertTrue(u instanceof URI);
            System.out.println(u.toString());
        }
    }

}

When the test is run, the output can be seen thus:

generateUris
http://server1/api/system
http://server1/api/system/system
http://server1/api/system/system/system
http://server1/api/system/system/system/system

What I would have expected is that it would return the same URI each time. In the real application these URIs would have more detail and each would have different data from a JPA class lookup. It seems that the same object is returned each time and then appended to again.

Is there a way to get Mockito to return a fresh URI each time uriInfo.getBaseUriBuilder() is called, or is this a fundamental misunderstanding I have about how Mockito works?

Answers

There are 1 suggested solutions here and each one has been listed below with a detailed description. The following topics have been covered briefly such as Unit Testing, Java, Mockito, Junit. These have been categorized in sections for a clear and precise explanation.

34

The problem you face is expected behavior. When setting up the mock you create a single instance and that is always returned.

To get what you want I would suggest using when(..).thenAnswer(i -> UriBuilder.fromUri(baseUri))

By this every call executes the provides lambda and it creates a new instance per invocation.