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

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?

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.

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.

Test a method without initialize the class

I'm very new to unit testing, I'm wondering if there is a way to test a method without initializing the class. The reason I'm asking is because there are lot of object passing in the constructor meaning a lot of mocking stubbing while from a thorough check methodToTest seems not to use any object attribute. It's not my code otherwise the method could be converted to static.
class ExampleClass {
public ExampleClass(FirstClass fc, SecondClass sc, ThirdClass tc) {}
public void methodToTest(FourthClass foc) {}
}
You have some options here:
Make the method static so you don't need a reference to an actual object. This will only work if the method does not need any state of ExampleClass (i.e. everything it needs is passed in the method arguments).
Extract this method to another class (perhaps using the method object pattern) that's easier to test on its own. This refactoring is called replace method with method object.
Usually, having to many parameters in constructors is a hint on bad conception. You'd better rethink you Objects and classes to reduce argument to give to the constructor.
If you don't want to, you can still use some kind of a "TestUtil" wich instantiate class for you.
Example :
public class MyTestUtils {
public createValidFirstClass() {
return new FirstClass(/* some arguments here */);
}
public createValidSecondClass() {
return new SecondClass(/* Some arguments here */);
}
public createValidThridClass() {
return new ThridClass(/* Some arguments here */);
}
public createValidExampleClass() {
return new ExampleClass(createValidFirstClass(), createValidSecondClass(), createValidThridClass());
}
}
This class MUST be in your test packages and not in your project, and should not be used outside of the tests, it would be a really bad practice here, use Factory or Builder for your real projects.
Anyway, i think that the best solution is to rethink you Classes.
Example :
public class People {
public People(String firstName, String lastName, Date birth, Date death) {
}
}
As you can see this is a pain in the ass to control that all given parameter was correctly formatted and not null.
This number of argument passed to a method can be reduced this way.
public class People {
public People(PeopleNames names, Period period) {
}
}
public class PeopleNames {
public People(String firstName, String lastName) {
}
}
public class PeopleNames {
public People(Date begin, Date end) {
}
}
Ok, it seems I found a way. Since the method is irrelevant to the state of the object, I could mock the object and order the MockRunner to use the real method when it is called. It is named partial mocking. The way of doing it is
ExampleClass = PowerMockito.mock(ExampleClass.class);
when(ExampleClass.methodToTest(foc)).thenCallRealMethod();
As far as I know, you cannot conduct a Test without initializing the class.
The three steps of Test are Arrange,Act and Assert. The class has to be initialized in the arrange part for fetching the required methods in the particular class you are testing.
I don't know what your method does but if it's possible for your application you could just make methodToTest static, which would allow you call it without an instance of the class.
Alternatively, you could avoid too much instantiation by creating one instance in a #BeforeClass method to be used for all tests, but again I don't know what you method does so that might not be desirable.
Use suppressions from PowerMockito.
import org.powermock.api.support.membermodification.MemberModifier;
import org.powermock.api.support.membermodification.MemberMatcher;
suppress(MemberMatcher.constructor(ExampleClass.class))

Could not set string value to setter

MyClass has a setter but when I assign a value to it I get the error:
java.lang.NoSuchMethodError: MyClass.setValue(Ljava/lang/String;)
MyClassIfc.java
public interface MyClassIfc extends EYSDomainIfc
{
public abstract String getValue();
public abstract void setValue(String paramString);
}
MyClass.java
public class MyClass implements MyClassIfc
{
protected String value = null;
public String getValue()
{
return this.value;
}
public void setValue(String paramString)
{
this.value= paramString;
}
}
Invoking sipnet:
MyClassIfc myClass = ((MyDomainObjectFactoryIfc)MyDomain.getFactory()).getMyInstance();
value = MyUtilities.getValue(utility, data, logger, serviceName);
myClass.setValue(value);
This means that you're trying to run a different version of that class from the one that you used at compile time. Clean out your build directories and recompile everything.
Can you share the code you did write in your class? Whenever you get NoSuchMethod the first thing you'd like to look at is the method signature.
For example:
Modifiers—such as public, private, etc: Make sure you get access modifier right, if you are calling the method creating an instance of the class (an object) and that method is private you won't be able to access it.
The method name: take a closer look to the method name and make sure you are typing it correctly. If you are using an IDE just let the autocomplete help you to avoid errors.
The parameter list their data types: Make sure the data-type of the variable/object you are passing to the method matches the datatype that the method has declared (what the method is expecting).
In your case, I would go for the parameter list first.
If you want to read more about method definition you can go to the java reference: http://docs.oracle.com/javase/tutorial/java/javaOO/methods.html
Hope this helps!

How to Avoid Constructor calling During Object Creation?

I want to avoid the constructor calling during object creation in java (either default constructor or user defined constructor) . Is it possible to avoid constructor calling during object creation???
Thanks in advance......
Simply extract the intialization logic that you want to avoid into another method called init. You can not avoid calling exactly one constructor.
No matter what pattern or strategy you use, at some point your will need to call a constructor if you want to create an object.
Actually, its possible under some circumstances by using classes from the JVM implementation (which do not belong to the JRE API and are implemenation specific).
One example here http://www.javaspecialists.eu/archive/Issue175.html
It should also be possible using sun.misc.Unsafe.allocateInstance() (Java7)
Also, the constructor is apparently bypassed when using the clone()-method to create a copy of an object (and the class doesn't override clone to implement it different from the Object.clone() method).
All of these possibilities come with strings attached and should be used carefully, if at all.
You can mock the constructors of a class. They will still be called, but not executed. For example, the following JUnit+JMockit test does that:
static class CodeUnderTest
{
private final SomeDependency someDep = new SomeDependency(123, "abc");
int doSomething(String s)
{
someDep.doSomethingElse(s);
return someDep.getValue();
}
}
static final class SomeDependency
{
SomeDependency(int i, String s) { throw new RuntimeException("won't run"); }
int getValue() { return -1; }
}
#Test
public void mockEntireClassIncludingItsConstructors()
{
new NonStrictExpectations() {
#Mocked SomeDependency mockDep;
{ mockDep.getValue(); result = 123; }
};
int result = new CodeUnderTest().doSomething("testing");
assertEquals(123, result);
}

Categories

Resources