I'm implementing an IntelliJ plugin for refactoring a huge amount of java classes.
Within a JavaRecursiveElementVisitor I want to change the signature of particular method calls.
Concretely, I want to change the parameter order for calls of static assertXxx methods in the context of upgrading from JUnit4 to JUnit5. Some test classes are already using JUnit5, so it is crucial to know which class was statically imported.
Probably this could be solved by using a ReferencesSearch, but I wonder if I can't get the class for which a static method is called, within the visitMethodCallExpression method via the PsiMethodCallExpression parameter:
#Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
// TODO: check if expression refers to org.junit.Assert.assertXxx or to org.junit.jupiter.api.Assertions.assertXxx
super.visitMethodCallExpression(expression);
}
I would prefer to stick on the visitor because since I have to do many changes within each class, I assume it would be more efficient to visit every class only once rather than invoking a ReferenceSearch again and again.
Related
It is possible in plain Java to override a method of a class
programmatically at runtime (or even create a new method)?
I want to be able to do this even if I don't know the classes at compile time.
What I mean exactly by overriding at runtime:
abstract class MyClass{
public void myMethod();
}
class Overrider extends MyClass{
#Override
public void myMethod(){}
}
class Injector{
public static void myMethod(){ // STATIC !!!
// do actual stuff
}
}
// some magic code goes here
Overrider altered = doMagic(
MyClass.class, Overrider.class, Injector.class);
Now, this invocation...
altered.myMethod();
...would call Injector.myMethod() instead of Overrider.myMethod().
Injector.myMethod() is static, because, after doing "magic"
it is invoked from different class instance (it's the Overrider),
(so we prevent it from accessing local fields).
You can use something like cglib for generating code on-the-fly
In java6 has been added the possibility to transform any already loaded class. Take a look at the changes in the java.lang.instrument package
For interfaces there is java.lang.reflect.Proxy.
For classes you'll either need a third-party library or write a fair bit of code. Generally dynamically creating classes in this way is to create mocks for testing.
There is also the instrumentation API that allows modification of classes. You can also modify classes with a custom class loader or just the class files on disk.
I wrote an article for java.net about how to transparently add logging statements to a class when it is loaded by the classloader using a java agent.
It uses the Javassist library to manipulate the byte code, including using the Javassist compiler to generate extra bytecode which is then inserted in the appropriate place, and then the resulting class is provided to the classloader.
A refined version is available with the slf4j project.
If I got it right, the main problem that concerns you is how to pass a static method delegate (like in C#), through the instance interface method.
You can check this article: A Java Programmer Looks at C# Delegates (archived), which shows you how to get a reference to your static method and invoke it. You can then create a wrapper class which accepts the static method name in its constructor, and implements your base class to invoke the static method from the instance method.
I'm using Dictionary class to localize my application, since I want my texts to be files outside of the application, so I can change them without the need to compile the app again.
To be able to get the strings, I created class StringIdentifiers, that had all the methods like:
public String minute(){
return getString("minute");
}
The method getString() basically just used a Dictionary to get the string from a JSON included into the page.
I then created subclass called Language, where I had some more logic (checking what language to use etc.). It all worked flawlessly, I used the Language subclass to access the Strings. Even in UIBinder.
Since there are more and more texts now, I decided to split these methods into separate classes. But since in Java I cannot do multiple inheritance, I decided to do it via interfaces. So instead of StringIdentifiers I created interfaces like: LoginTexts, MenuTexts, EventLogTexts etc. In these interfaces I specified default methods to get the appropriate strings. The Language class implements all the interfaces and also the getString() method.
Now it all works fine, I get instance of the Language class, I can use methods from the interfaces in the code, no errors.
BUT when it comes to UI binder, it doesn't show errors in editor, but at compilation it tells:
[ERROR] Could not find no-arg method named minute in type
com.company.project.client.locale.Language
Does this mean, that UI binder is not able to see inherited default method from the interfaces? Really? I mean in the Java code, it is working normally.
Im experiencing the same issue, however, after searching I discovered this clever work-around written by Jos31fr: https://github.com/gwtproject/gwt/issues/9629
Define the interface assuming that GWT supports default methods.
In the class implementing the interface, redefine the default method as a call to the super-class.
import com.google.gwt.user.client.ui.TextBox;
public class TestTextBox extends TextBox implements TestInterface {
public void setFoo(String bar) {
TestInterface.super.setFoo(bar);
}
}
In Swift, to test a default access level class, one can put #testable in the test class header, making the internal access level class accessible and testable from the test package, without everything in the class public. I was wondering if Java has a way to access the same purpose?
How can I test a default access level class in Java from the test package without making everything in the class public?
There are #VisibleForTesting annotations in some java libs, but generally it does not prevent illegal access. Even making package protected does not solve all the issues as still some other classes can use testing code, which can lead to some unexpected behaviour. I recently stumbled upon nice construct that allows you to show the intentions about exposing some methods for tests
public class A{
private int someMethodYouWantToTest(){}
private Testability testability = new Testability();
class Testability{
int exposedMethodForTest(){
someMethodYouWantToTest()
}
}
}
And then in your test class
public class Test{
private A underTest = new A()
public void testHiddenMethod(){
a.testability.exposedMethodForTest()
}
}
This way you private method is private, and only access if by dedicated testability inner class that clearly states its purpose, so no one by accident calls your method outside of tests. This solves issues with package protected businness methods that may be called from other places but were really meant to be private.
In Java, the only thing you can do is make things package protected if you want them to be used from your test code (that is: if you don't want them to be public).
Example: my classes very often look like
class Whatever
public Whatever() { this(new A(), new B()); };
Whatever(A a, B b) { ...
allowing me to use the second constructor for unit tests that require dependency injection; and at the same time relying on the "convention" that production code should prefer to always use the public constructor.
So even when I have classes that I don't want to be used outside of my package ... i make the constructor public to indicate: use this one please.
The idea is basically that your production code and test code resides in identically-named packages.
In other words: Java doesn't have this nice feature of giving access only to test code.
Quoting an answer to a similar question
"
You generally don't unit test private methods directly. Since they are
private, consider them an implementation detail. Nobody is ever going
to call one of them and expect it to work a particular way.
You should instead test your public interface. If the methods that
call your private methods are working as you expect, you then assume
by extension that your private methods are working correctly."
This is equivalent to option 1 in this link
If 1 does not fit your goals, you can try Approach 2,3 and 4 mentioned in the link
Sure it is not perfect that one has to make methods visible for testing that would otherwise be private, even if it is only in the classes own package.
On the other side, it is anyway recommended (and has many great benefits) not to depend on impelementations but on Interfaces.
That means: Give the client an Interface that declares only the methods you want to expose and make the methods you have to test in your implementation protected and do not include them in the interface.
As i understand it, the correct way to test private methods is via reflection? So if we test a method via reflection, we need to make an instance of the class, get the method, invoke it and so on. However, what do we do if the class that we create uses data from other objects that will return null / be null without the correct previously made objects.
Thanks
I personally think you should avoid writing tests on private methods. Using reflection to me is a design smell - you should ideally write tests only against the exposed public interface of your class.
If you feel the need to test a private method, then this suggests to me that you should do one of the following:
Recognise that the method really should be part of the public interface and make it public
Write a test against some existing public methods of the class that indirectly test the private method
Write a new public method that exposes the behaviour you wish to test
Refactor the behaviour out into another class which you can test (thanks Tom for the extra idea!)
As i understand it, the correct way to test private methods is via
reflection?
It's the only way (if you're only testing the private method, and not some other public method that calls the private method), as you cannot access the private methods from outside of the class (except with reflection). I don't usually write separate tests for private-methods, but it can be useful.
what do we do if the class that we create uses data from other objects
that will return null / be null without the correct previously made
objects.
In unit-tests, you "mock" the outside dependencies, either using a mocking-library, such as Mockito or some other, or write anonymous or separate mocking classes implementing the interface of the dependency. The idea is that you define some exact behavior for the outside dependencies, so their behavior won't affect the testing of the actual class. If the fields holding the references are private, you need to use reflection to inject the objects into them.
In integration-testing, you use the actual implementations of the outside dependencies.
You can use an existing instance of the class (say, the instance on which you've already tested the public methods).
Now just invoke the private method (found through reflection) of that instance, which, presumably, must have all the 'previously made' objects.
As part of a larger project, I am attempting to achieve something that I'm not sure is possible, so am eager to see if anyone has any suggestions!
The overall system:
As a whole, my system should be able to be provided with a JUnit test class, that matches some provided interface. Classes will be then given that do not implement this interface, but need to be checked to see if they would be able to (a.k.a. if they implement all necessary methods). If so, some transformation should take place such that the JUnit test class can be run against it.
So far I have implemented:
- A package that loads other classes given a path and name, using URLClassLoader
- A package that runs a JUnit test case and returns the results, using JUnitCore
The problem:
1. At first, how could I run the JUnit test against a class that does implement the interface when the test is designed to match the interface? How do I (at runtime) dictate that the instance being tested by the interface is the loaded class?
Is it possible to then extend this, such that I could i) verify that it does match the interface (I assume using Reflection to check for corresponding methods?) and then ii) modify that class such that it can be tested using the JUnit test class?
Thanks for any advice that might help towards part of this problem. I appreciate my description may be lacking, so please comment if you have any extra information that would help you give any answer!
You can do everything you want with the reflection API. It sounds like you should start with the tutorial, and then come back here for specific questions. Given a Class object you can check if it implements a given interface, create an instance of it, and then treat it like any other class.
Edit: I don't think I got that from your question, but in that case you are looking for the Proxy part of the reflection API.
how could I run the JUnit test against
a class that does implement the
interface when the test is designed to
match the interface
Since you have the class you can use the isAssignableFrom method offered by the class such that
Class loadedJunitClass = clazz;
MyInterface impl = null;
if(MyInterface.class.isAssignableFrom(loadedJunitClass )){
impl = (MyInterface) loadedJunitClass.newInstance();
}
For the second question, you can check each method and see 1. If there exists a method with the same method name as defined in the interface, 2. If the method return type is the same from the interface and 3. If the method parameter types and length are the same. Of course 2 and 3 can be tricky to get right.
At that point I would just create an instance of that interface (anonymous or a private class), create a newInstance of that matching class. And invoke the methods through reflection within the interface's methods.
Now that is how you can get it done with reflection. I am not advicating reflection as you can imagine :)
For the first part of your question; if you have the loaded Class instance for the class you want to test you can construct one with newInstance() if it has a default constructor, or via the getConstructor methods if you need to pass parameters. You should be able to get this Class instance from the class loader.
For the second part. You should be able to check the public methods via getMethods() (again on the Class instance) then look through the returned array for the methods you want. There are methods on the Method class that will return information about parameters, exceptions and return type to verify they are what you require.
However, I am pretty certain it is not possible to modify the class at runtime to add the interface. It might be possible by modifying the byte code, but I don't know about that.
An alternative would be to write your test to call all method via reflection, then it doesn't matter what the type of the object is just that it has the right methods (which you've already checked).
If you want to make arbitrary class to implement given interface at runtime if its public API matches the interface, you have several options in Java. Creating java.lang.Proxy to bridge the target class, exposing YourInterface is the easiest way.
YourInterface i = (YourInterface) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{YourInterface.class},
new InvocationHandler() {
#Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//run method on your target class here using reflection
}
});
You can also use mixins in AspectJ or subclass your target class using CGLIB and add interface at runtime. But the proxy approach is not that hard-core to implement.