user3809938
user3809938

Reputation: 1304

How to mock methods in dynamically loaded jars

I have a class called Price with constructor, which I am dynamically loading via reflection:

public Price(Context context, String pair) {
    this.context = context;
    this.value1 = pair.substring(0, 3);
    this.value2 = pair.substring(3, 6);
    this.dps = context.getService().getm1(value1, value2).getm2();
}

However I want to mock the Context object

and I want

context.getService().getm1(value1, value2).getm2()

to return 5.

Here is what I have tried

//mocking the Context class
Class<?> contextClass = urlClassLoader.loadClass("com.algo.Context");
constructor =contextClass.getConstructor();
Object context = Mockito.mock(contextClass);

//trying to instantiate the Price class
Class<?> priceClass = urlClassLoader.loadClass("com.algo.Price");
constructor = priceClass.getConstructor(contextClass,String.class);
Mockito.when(context.getService().getm1(value1, value2).getm2().thenReturn(5));
Object price = constructor.newInstance(context,"PRICES");

However I have a red line under

context.getService()

The error says

The method getService() is undefined for the type Object

How can I get around this, my end goal is to create the Price object with the variable

dps

being an int 5, that is why I want to mock the Context object.

Upvotes: 0

Views: 625

Answers (2)

Nicolas Filotto
Nicolas Filotto

Reputation: 44995

For me the only way is to implement your whole test using reflection which is really laborious especially in your case as you will need to do the same thing for each method call as you cannot mock directly context.getService().getm1(value1, value2).getm2().

Assuming that I have a class Context as below

public class Context {

    public int getm1(String value1, String value2) {
        return -1;
    }
}

A normal test case would be:

@Test
public void normal() throws Exception {
    Context context = Mockito.mock(Context.class);
    Mockito.when(context.getm1(Mockito.anyString(), Mockito.anyString())).thenReturn(5);
    Assert.assertEquals(5, context.getm1("foo", "bar"));
}

The same test using reflection would be:

@Test
public void reflection() throws Exception {
    ... // Here I get the classloader
    // Get the class by reflection
    Class<?> contextClass = urlClassLoader.loadClass("com.algo.Context");
    // Mock the class
    Object context = Mockito.mock(contextClass);
    // Get the method by reflection
    Method method = contextClass.getMethod("getm1", String.class, String.class);
    // Invoke the method with Mockito.anyString() as parameter 
    // to get the corresponding methodCall object
    Object methodCall = method.invoke(context, Mockito.anyString(), Mockito.anyString());
    // Mock the method call to get what we expect
    Mockito.when(methodCall).thenReturn(5);
    // Test the method with some random values by reflection
    Assert.assertEquals(5, method.invoke(context, "foo", "bar"));
}

Upvotes: 1

David Ibl
David Ibl

Reputation: 911

Cannot really understand this issue. If you are working with an unknown type it cannot be typed as Context within the construtor.

But independently, an approach would be to create interfaces representing the expected structure of context and then mock the interfaces to return the value. It is not necessary to really load the dynamic class within the test if it is mocked either way.

Upvotes: 0

Related Questions