1

I'm new to MongoDB and I'm writing a series of unit tests for a Mongo-backed REST web-service. Here's a simple test for a /clients/{id} enpoint :

@RunWith(MockitoJUnitRunner.class)
public class ClientsControllerMockMvcStandaloneTest {

    private MockMvc mvc;

    @Mock
    private ClientsRepository clientsRepository;

    @Mock
    private ModelMapper modelMapper;

    @InjectMocks
    private ClientsController clientsController;

    private ExceptionHandlerExceptionResolver createExceptionResolver() {

        ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {

            @SuppressWarnings("ConstantConditions")
            @Override
            protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
                                                                              final Exception exception) {

                final Method method = new ExceptionHandlerMethodResolver(RestResponseEntityExceptionHandler.class)
                        .resolveMethod(exception);

                final RestResponseEntityExceptionHandler handler = new RestResponseEntityExceptionHandler();

                return new ServletInvocableHandlerMethod(handler, method);
            }
        };

        exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        exceptionResolver.afterPropertiesSet();

        return exceptionResolver;
    }

    @Before
    public void setup() {

        JacksonTester.initFields(this, new ObjectMapper());

        mvc = MockMvcBuilders.standaloneSetup(clientsController)
                .setHandlerExceptionResolvers(createExceptionResolver())
                .build();
    }

    // GET /api/clients/{id} 200

    @Test
    public void findById_ClientEntryFound_ShouldReturnFoundClientEntry() throws Exception {

        final ObjectId id = new ObjectId();

        final Client client = Client.builder()
                .id(id)
                .name("Microsoft")
                .build();

        final ClientDTO clientDTO = ClientDTO.builder()
                .id(id)
                .name("Microsoft")
                .build();

        when(clientsRepository.findById(id))
                .thenReturn(Optional.of(client));

        when(modelMapper.map(client, ClientDTO.class))
                .thenReturn(clientDTO);

        mvc.perform(get("/clients/" + id.toString())
                .accept(TestUtils.APPLICATION_JSON_UTF8))
                .andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id", is(id)))
                .andExpect(jsonPath("$.name", is("Microsoft")))
                .andDo(MockMvcResultHandlers.print());

        verify(modelMapper, times(1)).map(client, ClientDTO.class);
        verify(clientsRepository, times(1)).findById(id);

        verifyNoMoreInteractions(clientsRepository);
    }
}

I expect this to work but I'm getting the following :

java.lang.AssertionError: JSON path "$.id"
Expected: is <5c9b9a0289d2b311b150b92c>
     but: was <{timestamp=1553701378, machineIdentifier=9032371, processIdentifier=4529, counter=5290284, timeSecond=1553701378, time=1553701378000, date=1553701378000}>
Expected :is <5c9b9a0289d2b311b150b92c>
Actual   :<{timestamp=1553701378, machineIdentifier=9032371, processIdentifier=4529, counter=5290284, timeSecond=1553701378, time=1553701378000, date=1553701378000}>
 <Click to see difference>

Any help would be appreciated (including any pointers if you think my general approach could be improved!).

Cheers!

1 Answer 1

5

Jackson doesn't know your ObjectId instance should be serialized as 5c9b9a0289d2b311b150b92c and not as:

{
  "timestamp": 1553701378,
  "machineIdentifier": 9032371,
  "processIdentifier": 4529,
  "counter": 5290284,
  "time": 1553701378000,
  "date": 1553701378000,
  "timeSecond": 1553701378
}

Luckily it's easy to fix. The ObjectId#toString() method (which will internally invoke ObjectId#toHexString()) allows you to convert the ObjectId instance into a 24-byte hexadecimal string representation.

So you could use @JsonSerialize along with ToStringSerializer to have the ObjectId instance represented as a string:

@JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;

Then, in your test, use the ObjectId#toString() method (or ObjectId#toHexString()) for the assertion:

.andExpect(jsonPath("$.id", is(id.toString())))

Alternatively, assuming that you are using Spring Data for MongoDB, instead of ObjectId, you could use:

@Id
private String id;

You also could handle the conversion of ObjectId to String in your mapper layer.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks but that's not it ! The caller gets the whole ObjectId instance { timestamp, machineIdentifier, processIdentifier, counter, time, date, timeSecond } ...
@ChambreNoire I see what you mean. I've updated my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.