Home > front end >  Getting a No Serializer Found for class when trying to test a method with ObjectMapper
Getting a No Serializer Found for class when trying to test a method with ObjectMapper

Time:09-27

I am getting this exception - java.lang.IllegalArgumentException: No serializer found for class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.mockito.codegen.Object$MockitoMock$["mockitoInterceptor"]->org.mockito.internal.creation.bytebuddy.MockMethodInterceptor["serializationSupport"])

The Testcase I'm writing looks like this

@Mock
Object object;

@Mock
ResponseType response;

@Test
public void handleRequest() {
    ObjectMapper mapper = new ObjectMapper();
    when(mapper.convertValue(object, ResponseType.class)).thenReturn(response)
    new Handler().handleRequest(object);
}

The method I'm trying to test:

public class Handler{

    public ResponseType handleRequest(Object object) {
        ObjectMapper mapper = new ObjectMapper();
        ResponseType response = mapper.convertValue(object, ResponseType.class);
        return response;
    }
}

I see that I can disable SerializationFeature.FAIL_ON_EMPTY_BEANS, but I have no control over the base class, I can only write a test case. Can someone tell me what I am doing wrong and what I can add to the test case? I cannot instantiate an ObjectMapper within the testcase as it has to be a mock, and I tried using a spy on the ObjectMapper and disable the SerializationFeature.FAIL_ON_EMPTY_BEANS, but neither works. I am also pretty new to Mockito so I'm not sure how I can proceed further. Any help would be appreciated, thanks

CodePudding user response:

There are two problems with your test code. You're calling the real ObjectMapper method instead of mocking it (1) and you're not using the created object (even though it's not a mock) in your actual code (2).


In the lines below (I've added newlines and a comment, but it's the same as your code) you're creating an instance of ObjectMapper (not a mock) and while trying to mock it's behavior, you're actually calling the real method of the created object:

ObjectMapper mapper = new ObjectMapper();
when(
    // here an actual convertValue method is called on the mapper object
    // which causes the exception you're getting
    mapper.convertValue(object, ResponseType.class)
).thenReturn(response)

If you used a method that's not calling the actual method while mocking (useful when working with spies):

doReturn(response)
        .when(mapper)
        .convertValue(object, ResponseType.class);

you'd still get an error, but this time it would be:

org.mockito.exceptions.misusing.NotAMockException: 
Argument passed to when() is not a mock!
Example of correct stubbing:
    doThrow(new RuntimeException()).when(mock).someMethod();

That's because the mapper you're creating is not a Mockito mock/spy, so it cannot be mocked. To fix that you need to use the mock(...) method or the @Mock annotation (remember to initialize/open them):

ObjectMapper mapper = mock(ObjectMapper.class);
when(mapper.convertValue(object, ResponseType.class))
        .thenReturn(response);

Now neither the initial error (that you were getting) nor NotAMockException are thrown, but the mocking has no effect.


In your actual code here:

public ResponseType handleRequest(Object object) {
    ObjectMapper mapper = new ObjectMapper();
    ...
}

you're creating ObjectMapper using new. This object is in no way connected to the mock that is created in the test - it is not injected. To fix that, it would be best to store the ObjectMapper instance in a field and initialize it with the class creation (injection) - both in the actual code and in the tests:

class Handler {

    private ObjectMapper objectMapper;

    Handler(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
    
    public ResponseType handleRequest(Object object) {
        // use the objectMapper here
    }
}

Thanks to that you can pass the mocked ObjectMapper in the test to the tested object constructor:

@Test
public void handleRequest() {
    ObjectMapper mapper = mock(ObjectMapper.class);
    when(mapper.convertValue(object, ResponseType.class)).thenReturn(response);

    new Handler(mapper).handleRequest(object);
}

Other ways of doing that could be using a factory providing the ObjectMapper, also mocked in the tests, or (not recommended) using mockito-inline with it's mockConstruction method (since Mockito 3.5.0).

  • Related