Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking getClass()

In Java I want to write test for method (simplified snippet):

public class MyClass {
    private static final Set<Class> SOME_SET = new HashSet<Class>(Arrays.asList(Foo.class, Bar.class));

    public boolean isValid(Class clazz){
        return SOME_SET.contains(clazz);
    }
}

The problem with following test

import static org.mockito.Mockito.when;
import org.mockito.Mockito;

public class MyClassTest {
    @Test
    public void isValid_Foo_returnsTrue(){
        Foo foo = Mockito.mock(Foo.class);
        MyClass target = new MyClass();
        assertTrue(target.isValid(foo));
   }
}

is that on mocked class Foo, foo.getClass() returns the class name with additional suffix. Something like this:

Foo$$EnhancerByMockitoWithCGLIB$$45508b12

Because of this reason test fails because SOME_SET.contains(clazz) returns false.

I was unable to mock getClass() method on Foo:

Mockito.when(foo.getClass()).thenReturn(Foo.class);

Because compiler was complaining: The method thenReturn(Class<capture#1-of ? extends Foo>) in the type OngoingStubbing<Class<capture#1-of ? extends Foo>> is not applicable for the arguments (Class<Foo>)

The question is, how to achieve that getClass() method of mocked object returns same value as getClass() method on real(non mocked) object?

like image 815
zoran Avatar asked Jul 31 '17 07:07

zoran


People also ask

What does the getClass method do?

getClass() method returns the runtime class of an object. That Class object is the object that is locked by static synchronized methods of the represented class.

What is object getClass?

The Java Object getClass() method returns the class name of the object.

How do you mock a final method?

Configure Mockito for Final Methods and Classes Before we can use Mockito for mocking final classes and methods, we have to configure it. Mockito checks the extensions directory for configuration files when it is loaded. This file enables the mocking of final methods and classes.


2 Answers

Mockito.mock(Foo.class) results a object in type of Foo, but not exactly Foo class. As at the background it will create an anonymous proxy, which is a subclass of Foo. So, the class isn't the same.

One note to your implementation of isValid is that: do you really want to check the class, or just to check the type (that will also accept subclass, isAssignableFrom) of the class. If you check for type matching, then you can test your method with mocked class.

Also, do you think that you can somehow work on objects rather than classes (e.g. isValid(object))? Using object is a better approach in OOP than clazz in my perspective. I would suggest:

    public boolean isValid(Object obj) {
        return SOME_SET.stream().anyMatch(clazz -> clazz.isInstance(obj));
    }
like image 157
Duong Nguyen Avatar answered Oct 09 '22 01:10

Duong Nguyen


I see no sense on mocking Foo on your scenario. Just pass Foo.class as param. You are checking MyClass.isValid logic, and you are not gonna check any interaction on Foo.

like image 27
albert_nil Avatar answered Oct 09 '22 02:10

albert_nil