How to mock methods in dynamically loaded jars - java

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.

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"));
}

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.

Related

Test an overridden method of an inner class with JUnit and Mockito

I have a method in a class I need to test. The method uses an external class that I need to mock, so the external class doesn't get tested or executes its dependencies. The special challenge is: one method of the external class gets overridden. Method looks like this:
public void fetchLocalData(final String source, final ObservableEmitter<String> destination) {
final List<String> options = Arrays.asList("recursive","allFiles","includeDir");
// This class comes from a package
final DirScan dirscan = new DirScan(source, options) {
#Override
protected Action getResult(final String result) {
destination.onNext(result);
return Action.Continue;
}
};
dirscan.scan();
destination.onComplete();
}
I tried:
DirScan scanner = mock(DirScan.class);
when(scanner.scan()).thenReturn("one").thenReturn("two");
That didn't work. What do I miss? How would I need to refactor to make this testable?
If you want to replace the dirscan with a mock (or a spy) you'll need to refactor your class that it's a dependency or parameter. Alternatively you could use PowerMockito's whenNew functionality.
Lets assume you change your class and instead of the String source you provide the DirScan object as a parameter. You would need to have some kind of creation method for dirscan elsewhere (might be a static method).
final List<String> options = Arrays.asList("recursive","allFiles","includeDir");
public DirScan createDirScan(String source) {
// This class comes from a package
final DirScan dirscan = new DirScan(source, options) {
#Override
protected Action getResult(final String result) {
destination.onNext(result);
return Action.Continue;
}
};
return dirscan;
}
public void fetchLocalData(final DirScan dirscan, final ObservableEmitter<String> destination) {
dirscan.scan();
destination.onComplete();
}
Juding from your question you seem to want to test the interaction with the destination object, so you do not want to mock the dirscan object (because if you do there won't be any interaction). You might want to use a spy and only replace the getResult method.
In your test now you could then simply pass a spy for the dirscan object and
define the behaviour of it with thenAnswer.
final ObservableEmitter<String> destination = ...
DirScan dirscan = Mockito.spy(createDirScan(source, destination));
Mockito.when(dirscan.getResult(Mockito.any(String.class))).thenAnswer((Answer<Action>) invocation -> {
String result = invocation.getArgument(0);
destination.onNext(result);
return Action.Continue;
});
classUnderTest.fetchLocalData(dirscan, destination);
At this point you might notice that its probably better to not use a spy and just use the real DirScan object. Using the spy to do what you intend to do with the overriden method looks like overkill to me.
The real object has to work for this test to be of value, so you might as well test the real thing.

How to refer a private method for a Supplier or Consumer in java

I want to reuse a piece of code written in Private method of another class A. Like
class A
{
private String method(String data){
return "abcd";
}
}
List myList= getListFromSomeSource();
myList.stream()
.map(A::method)
.collect()....etc
The only way to access a private method of a class, if the class implementation does not provide such an option and if that implementation cannot be modified, is via reflection.
Assuming that the method function of class A has a String return type, a simple way to do so is
public static String invokeMethod(A object, String data) throws Exception {
Method method = A.class.getDeclaredMethod(“method”, String.class);
method.setAccessible(true);
return (String) method.invoke(object, data);
}
Since the Class A method in question is not static, an object reference would need to be used to access it, with or without reflection, e.g.
A object = new A(); // Create object of type A
String data = “...”; // Specify data input
String result = invokeMethod(object, data); // Call method
If such an object of type A cannot be created, or if the caller does not want to pass to invokeMethod a reference to such an object, the only other option left is actually rewriting the method function, outside Class A.

How to pass a method as an argument to a different method?

I have a method let's say in ClassA. I want to pass a method from ClassB as an argument to that method in ClassA. In this case I want to pass the getCode method from ClassB. I don't have any instance of ClassB and I'd like to achieve this without having to create one.
I've tried using simple method reference, but it does not work this way.
I don't want to make getCode a static method either.
public class ClassA {
public void validate() {
Validation validation = new Validation(ClassB::getCode, code);
//...
}
}
My final goal is to have a RequestValidator class to which add validations, each validation will be created with a specific method and a string in its constructor, in this case getCode from classB and code. Please note I only want one instance of RequestValidator. Something like this:
RequestValidator validator = new RequestValidator<>()
.addValidation(new Validation(ClassB::getCode, code))
.addValidation(new Validation(ClassB::getName, name));
getCode needs to be a static function, and the syntax would be ClassB.getCode. You would need ClassB to be imported into ClassA.
See:
Calling static method from another java class
Your use of a method reference will work just fine as long as you define the method arguments properly. You haven't given a lot of information, so I'm going to make some assumptions here. Please correct me if this isn't what you had in mind:
public class B {
public static String getCode() {
return "foobar"; // Replace with your own functionality
}
}
public class Validation {
Validation(Supplier<String> supplier, String code) {
String suppliedCode = supplier.get();
// Do your validation logic
}
}
public static void validate() {
Validation validation = new Validation(ClassB::getCode, code);
}
But this frankly feels like overkill. Why can't you just make your Validation constructor take two String arguments (or whatever types you happen to be using), and then do this?
public static void validate() {
Validation validation = new Validation(ClassB.getCode(), code);
}
Do you have a legitimate need to pass in a method reference instead of simply passing in the return value from the method call?

Instantiating an Object using Java Reflection

I am testing a private method using JUnit and I am invoking it using Reflection. The error I am getting is java.lang.InstantiationException. I know it is not creating an instance of Class but I am not sure what I am doing wrong. Object object = clazz.newInstance(); is the line that throws Exception.
Method under test
private int _getType(String type) {
if ("DATE".equalsIgnoreCase(type)) return Types.DATE;
if ("STRING".equalsIgnoreCase(type)) return Types.VARCHAR;
if ("INT".equalsIgnoreCase(type)) return Types.INTEGER;
if ("TIMESTAMP".equalsIgnoreCase(type)) return Types.TIMESTAMP;
return Types.NULL;
}
JUnit test
#Test
public void testGetType() throws Exception {
String type1 = "DATE";
String type2 = "STRING";
String type3 = "INT";
String type4 = "TIMESTAMP";
Class clazz = SpringStoredProcImpl.class;
Object object = clazz.newInstance();
Method method = object.getClass().getDeclaredMethod("getType", String.class);
method.setAccessible(true);
method.invoke(object, type1);
I don't have my asserts yet so please ignore it.
Thanks in advance.
You try to create an instance with a no argument constructor which does exist in your case.
As the constructors are public you should first create your object normally using the new keyword, then execute the rest of your code starting from Method method...
FYI, if you wanted to create your object by reflection it would be something like clazz.getConstructor(DataSource.class, String.class, ArrayList.class).newInstance(dataSource, sprocName, params) instead of simply clazz.newInstance()
Different answer: don't do that.
Why do you think you need to make this method private? It looks like the responsibility of this method is to "lookup" an enum type, based on string input. I think it would make much more sense to simply make this method package protected and avoid the reflection overhead/hassle.

Can one use Mockito to mock org.jibx.runtime.BindingDirectory?

I'm writing unit tests for my Java app and I need to write a test for a potential JiBX exception that can be thrown. The method I'm testing calls on a method from another class, where the JiBX exception can be potentially thrown. This is the class I'm testing (let's call it Class A):
#Inject
private CommonDAL commonDAL;
#Async
public Future<String> getTransactionalXXXAvailability(
List<XXXAvailRequestEntry> requestEntries, TravelWindow travelWindow) {
if (requestEntries.size() == 0)
return null;
XXXAvailRqAccessor requestAccessor = new XXXAvailRequestBuilder().buildRequest(requestEntries, travelWindow);
logger.info(requestAccessor.marshalRequest());
String responseAsXml = null;
try {
responseAsXml = getResponse(requestAccessor.getRequest());
} catch (JiBXException e) {
logger.error("Problem unmarshaling the XXX avail response: ", e);
}
logger.info(responseAsXml);
return new AsyncResult<String>(responseAsXml);
}
private String getResponse(OTAXXXAvailRQ request) throws JiBXException {
HbsiConnectionInfo connectionInfo = new HbsiConnectionInfo();
connectionInfo.useConnectionInfoFromContext();
HBSIXML4OTAWebserviceSoap hbsiSoap = getHbsiSoapService(connectionInfo);
InterfacePayload header = new InterfacePayload();
header.setChannelIdentifierId("XXXXXXXXX");
header.setVersion("2005B");
header.setInterface("HBSI XML 4 OTA");
ComponentInfo componentInfo = new ComponentInfo();
XXXAvailRqAccessor requestAccessor = new XXXAvailRqAccessor(request);
componentInfo.setId(requestAccessor.getFirstXXXCode());
componentInfo.setUser( connectionInfo.getUsername() );
componentInfo.setPwd( connectionInfo.getPassword() );
componentInfo.setComponentType(EComponentType.XXX);
Login login = new Login();
login.setComponentInfo(componentInfo);
Message body = new Message();
// todo: this needs to be unique for every request.
// todo: hook up to logging
body.setRequestId(UUID.randomUUID().toString());
body.setTransaction(ETransaction.XXX_AVAIL_RQ);
body.setXML(requestAccessor.marshalRequest());
return hbsiSoap.getSoapRequest(header, body, login);
}
HBSIXML4OTAWebserviceSoap getHbsiSoapService(HbsiConnectionInfo connectionInfo) {
HBSIXML4OTAWebservice ws = new HBSIXML4OTAWebservice( connectionInfo.getWsdlLocation() );
HBSIXML4OTAWebserviceSoap hbsiSoap = ws.getHBSIXML4OTAWebserviceSoap();
Map<String, Object> requestContext = ((BindingProvider)hbsiSoap).getRequestContext();
String readTimeout = commonDAL.getPropertyValue(new PropertyKey(Section.HBSI,
Property.HBSI_WS_READ_TIMEOUT));
requestContext.put(BindingProviderProperties.REQUEST_TIMEOUT, Integer.parseInt(readTimeout));
String connectionTimeout = commonDAL.getPropertyValue(new PropertyKey(Section.HBSI,
Property.HBSI_WS_CONNECTION_TIMEOUT));
requestContext.put(BindingProviderProperties.CONNECT_TIMEOUT, Integer.parseInt(connectionTimeout));
return hbsiSoap;
}
The method that throws the error is as follows (and from another class, let's call it Class B):
public String marshalRequest() {
StringWriter requestAsXml = new StringWriter();
try {
IBindingFactory bindingFactory = BindingDirectory.getFactory(PROTECTEDCLASSNAME.class);
IMarshallingContext marshalingContext = bindingFactory.createMarshallingContext();
marshalingContext.setIndent(2);
marshalingContext.setOutput(requestAsXml);
marshalingContext.marshalDocument(request);
} catch (JiBXException e) {
logger.error("Problem marshaling PROTECTEDCLASSNAME.", e);
}
return requestAsXml.toString();
}
When "body.setXML(requestAccessor.marshalRequest());" is called, another class (requestAccessor) is visited by the test, and it's method .marshalRequest is where the JiBX exception is supposed to be thrown. The purpose of the tests I'm writing is to get this Class A's unit test coverage to 100&, but the system under test is composed of at least two classes as I can't mock XXXAvailRqAccessor object called requestAccessor. I can't get any tests to produce this error, for the following reasons.
The XXXAvailRqAccessor object called requestAccessor is instantiated inside the methods I'm testing so I can't use a mock to throw an exception.
the OTAXXXAvailRQ argument passed to .getResponse() cannot be mocked because it's created by the builder for XXXAvailRqAccessor.
I tried spying on IBindingFactory, but it didn't work. I created a method in Class B that would instantiate an IBindingFactory so I could spy on it, but that didn't work.
I also tried using PowerMock to return a mock XXXAvailRqAccessor when it is instantiated, however when I attempted to mock a JiBXExceptioin for .getRequest, Mockito said "Checked exception is invalid for this method". If I can't get Mockito to throw this error, I don't know if it's possible to manipulate the associated objects to throw it.
Well not really, or I don't know of such way at least. You could, if you REALLY want to do it (I'm against it) create a method like this in that class:
IBindingFactory getBindingFactory() {
return BindingDirectory.getFactory(PROTECTEDCLASSNAME.class);
}
And replace this line:
IBindingFactory bindingFactory = BindingDirectory.getFactory(PROTECTEDCLASSNAME.class);
With:
IBindingFactory bindingFactory = getBindingFactory();
Then you can spy() (you can read on Mockito.spy() in the documentation if you're not familiar with it) this object and make this method return a mock. From that point it's smooth sailing.
This approach is not adviced though because:
you are creating a new method (a useless one) just for testing
said method has to be visible for testing so you cannot mark it as private...
I'm not a huge fan of spies in general
The question remains: how to properly test such cases. Well in most situations I try to refactor as much as possible and sometimes it helps. And in other cases... Well I still did not come up with a better solution.
As I said in my comment, I fully advocate Mateusz's solution. But there is an alternative. In the class that has the marshalRequest method, have a private final field of type IBindingFactory. Also in this class, have a package-private constructor with one extra argument, that is, the IBindingFactory to set. The normal constructor will call BindingDirectory.getFactory( ... ) then call the new constructor. So, if the standard constructor has a single String argument, the class might look like this.
public class MyClass{
private String name;
private IBindingFactory bindingFactory;
public MyClass(String name){
this(name, BindingDirectory.getFactory(PROTECTEDCLASSNAME.class));
}
MyClass(String name, IBindingFactory bindingFactory){
this.name = name;
this.bindingFactory = bindingFactory;
}
public String marshalRequest() {
StringWriter requestAsXml = new StringWriter();
try {
IMarshallingContext marshalingContext = bindingFactory.createMarshallingContext();
marshalingContext.setIndent(2);
marshalingContext.setOutput(requestAsXml);
marshalingContext.marshalDocument(request);
} catch (JiBXException e) {
logger.error("Problem marshaling PROTECTEDCLASSNAME.", e);
}
return requestAsXml.toString();
}
}
The reason for doing this is so that in the test for your class, you can create a mock IBindingFactory, and pass it in to the package-private constructor.

Categories

Resources