Can't update a class method with javassist - javassist

I'm playing with javassist (to use it later on a project) but I don't manage to make a simple update to a class.
I try to insert code before a method but its not being executed.
I've a gradle project and I'm using javassist version: '3.27.0-GA'.
Given the following class:
public class Dummy{
public int dummy(){
return 5;
}
}
The following test fails, so the class is not being modified:
#Test
public void modifyReturnValueTest() throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Dummy");
CtMethod m = cc.getDeclaredMethod("dummy");
m.insertBefore("{ if(true) return 3; }");
cc.writeFile();
assertEquals(3, new Dummy().dummy());
}
I'm missing something?

My guess is that when you call new Dummy().dummy() in
assertEquals(3, new Dummy().dummy());
the class loader loads the original version of Dummy class.
Since you want to load/use the version that you modified represented by the CtClass instance cc, you can insert the following snippet before assertEquals to make the class loader load the modified version of the Dummy class instead:
cc.toClass();
then the assert should be successful.
Note that in order to use toClass() above we rely on the fact that the Dummy class is never loaded before the toClass() invocation. (It will throw exception otherwise, the class loader cannot load two different versions of the same class at the same time)
You can check the javassist documentation for more details. This section might be especially useful http://www.javassist.org/tutorial/tutorial.html#load.

Related

bytebuddy official demo throw Exception "Class already loaded: class foo.Bar"

Section "Working with unloaded classes" in Official document give a demo, I run it on my machine then throw an exception Class already loaded: class foo.Bar。
class MyApplication {
public static void main(String[] args) {
TypePool typePool = TypePool.Default.ofSystemLoader();
new ByteBuddy()
.redefine(typePool.describe("foo.Bar").resolve(), // do not use 'Bar.class'
ClassFileLocator.ForClassLoader.ofSystemLoader())
.defineField("qux", String.class) // we learn more about defining fields later
.make()
.load(ClassLoader.getSystemClassLoader());
assertThat(Bar.class.getDeclaredField("qux"), notNullValue());
}
}
bytebuddy version is 1.10.22
The problem is in the last line Bar.class.getDeclaredField("qux") which loads the Bar class upon validation of the code. I fixed this in the example. Rather use the return value of load which returns Bar.

When I subclass a class using ByteBuddy in certain situations I get IllegalAccessErrors. Why?

(I am a new ByteBuddy user. I'm using ByteBuddy version 1.10.8 and JDK 11 without the module path or any other part of the module system.)
I have a nested class declared like this:
public static class Frob {
protected Frob() {
super();
}
public String sayHello() {
return "Hello!";
}
}
(Its containing class is foo.bar.TestExplorations.)
When I create a dynamic subclass of Frob named foo.bar.Crap like the following, everything works OK as I would expect:
final String className = "foo.bar.Crap";
final DynamicType.Unloaded<?> dynamicTypeUnloaded = new ByteBuddy()
.subclass(Frob.class)
.name(className)
.make();
final Class<?> mySubclass = dynamicTypeUnloaded
.load(this.getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
assertNotNull(mySubclass);
assertEquals(className, mySubclass.getName());
final Object frobSubclass = mySubclass.newInstance();
assertTrue(frobSubclass instanceof Frob);
But if I change Frob's constructor so that it is package private, I get the following error from the final assertion:
java.lang.IllegalAccessError: class foo.bar.Crap tried to access method 'void foo.bar.TestExplorations$Frob.<init>()' (foo.bar.Crap is in unnamed module of loader net.bytebuddy.dynamic.loading.ByteArrayClassLoader #5e3d57c7; foo.bar.TestExplorations$Frob is in unnamed module of loader 'app')
For some reason, Crap's constructor cannot call super(), even though Crap and Frob are in the same package, and Frob() is defined as package-private.
I have a sense the JDK module system is to blame here, even though I am deliberately (very, very deliberately) not using it. I know the module system does not like split packages, which is what it looks like to me is going on here. Is there a constructor strategy or other mechanism to work around this problem?
In Java, a package is only equal to another package if it has the same name and is loaded by the same class loader (the same as it is with classes). If you are using the WRAPPER strategy, you cannot access package-private members of any super class. Byte Buddy does not forbid the generation as it would be legal to do in javac but you would need to use the INJECTION strategy to do what you want to make sure that classes are loaded by the same class loader. Mind that it uses internal API, therefore, from Java 9, you'd rather use a ForLookup class loading strategy.

How did TestNg annotation mentioned in one class get executed from another from another class?

While learning Testng on Udemy, I come across a code which I am unable to understand. Instructor has created class named "testcore" where he defined #BeforeMethod/#aftermethod.Later he created another class named "LoginTest" where he wrote actual test with #test. He extended testcore class in loginTest to get variable initiated in testcore class. When he ran loginTest then #BeforeMethod/#aftermethod also ran with this. How did these two method ran along with #test when these methods are in different class.
here are both codes:
public class testcore {
public static Properties config = new Properties();
public static Properties obj = new Properties();
public static Xls_Reader xls = null;
public static WebDriver driver;//=null;
#BeforeMethod
public void init() throws Exception{
if(driver==null) {
// Loading Config Properties File
File Config_f = new File(System.getProperty("user.dir")+"\\src\\dd_Properties\\config.properties");
FileInputStream fs = new FileInputStream(Config_f);
config.load(fs);
// Loading Object Properties File
File Obj_f = new File(System.getProperty("user.dir")+"\\src\\dd_Properties\\Object.properties");
fs = new FileInputStream(Obj_f);
obj.load(fs);
//Loading xlsx file
xls = new Xls_Reader(System.getProperty("user.dir")+"\\src\\dd_Properties\\Data.xlsx");
System.out.println(config.getProperty("browerName"));
if(config.getProperty("browerName").equalsIgnoreCase("Firefox")) {
driver = new FirefoxDriver();
}
else if(config.getProperty("browerName").equalsIgnoreCase("Chrome")) {
driver = new ChromeDriver();
}
else {
throw new Exception("Wrong/No Browswer sepcified");
}
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
}
#AfterMethod
public void quitdriver() throws EmailException {
driver.quit();
//monitoringMail.Sendmail();
}
Here are LoginTest class:
public class LoginTest extends testcore {
#Test
public void doLogin() {
driver.findElement(By.xpath(obj.getProperty("LoginBtn"))).click();
driver.findElement(By.xpath(obj.getProperty("username"))).sendKeys();
driver.findElement(By.xpath(obj.getProperty("password"))).sendKeys();
}
1) How did these two method ran along with #test.
First of all LoginTest extends the testcore .
Due to this we can inherit the method of testcore in our LoginTest class.
2) When he ran loginTest then #BeforeMethod/#aftermethod also ran with this.
Please refer below details
Configuration information for a TestNG class:
#BeforeSuite: The annotated method will be run before all tests in this suite have run.
#AfterSuite: The annotated method will be run after all tests in this suite have run.
#BeforeTest: The annotated method will be run before any test method belonging to the classes inside the <test> tag is run.
#AfterTest: The annotated method will be run after all the test methods belonging to the classes inside the <test> tag have run.
#BeforeGroups: The list of groups that this configuration method will run before. This method is guaranteed to run shortly before the first test method that belongs to any of these groups is invoked.
#AfterGroups: The list of groups that this configuration method will run after. This method is guaranteed to run shortly after the last test method that belongs to any of these groups is invoked.
#BeforeClass: The annotated method will be run before the first test method in the current class is invoked.
#AfterClass: The annotated method will be run after all the test methods in the current class have been run.
#BeforeMethod: The annotated method will be run before each test method.
#AfterMethod: The annotated method will be run after each test method.
It's very simple inheritance related question. When you have extended testcore class in LoginTest class all the methods and data member available in parent will be available to child class including methods annotated with #Before Method etc.
I think you are confused due to miss concept regarding the way TestNG run a program. There are different different way to run a testNG annotated program to get executed and among them XML is a way. So don't get get confuse that all class, methods need to include in that xml. You just need an entry point only and rest will call accordingly.

General Fixture Setup with PowerMockito and #BeforeClass

I have a test where I configure some general fixtures however after using PowerMockRule the static variables which I configure in my #BeforeClass method reset to null. This causes the following test to fail however if you remove PowerMockRule it passes.
public class Test
{
#Rule
public PowerMockRule rule = new PowerMockRule();
private static String MyString;
#BeforeClass
public static void setupClass() throws Exception
{
MyString = "FOO";
}
#org.junit.Test
public void test() throws Exception
{
assertEquals("FOO", MyString);
}
}
I have the answer, but you are not going to like it.
Short answer:it looks like a defect in PowerMock, so create a issue in our bug tracker
Long answer: As you may know the PowerMock to be able mock static, private and so on loads classes by custom class loader and modified byte code. Then #PowerMockRunneris used then PowerMock can control loading a test class and the test class is also loaded by custom class loader. In case if another jUnitRunner runs test and the PowerMockRuleis used, then the test class and all other classes that is needed for test are loaded with standard class loader. PowerMock reloads all these classes either by using deep coping with serializing/deserializing or by using objenesis. So as class is reloaded all static fields which was initialised are null.
I've briefly checked code and I haven't found test for your cases and that we processed #BeforeClass, so create a issue in our bug tracker and I'll check it deeply.
By the way, please, also point which version do you use and which dependencies do you use.

How JUnit TestCase functionality actually works?

I have a code like this:
public class MyTest extends TestCase {
private MyObject mObject1;
private MyObject mObject2;
...
#Override
public void setUp() throws Exception {
super.setUp();
}
public void testSomething() {
mObject1 = new MyObject();
mObject2 = new MyObject();
}
public void testSomething2() {
// Here I can't access the previously created objects mObject1 and
// mObject2, because they are again null.
// Why is that, if *my* setUp() method doesn't touch them?
}
My guess is that JUnit instantiates the class again every time. Can someone please explain me the workflow?
Thanks.
JUnit will instantiate the class (MyTest) once per test and then execute the methods
setUp()
testXXX()
tearDown()
until it runs all the methods that start with test and don't receive any parameters. So in your example, Junit will instantiate MyTest twice. You can read more about this in the JUnit documentation.
Bear in mind that this is the old way of writing tests. From Junit 4 (I think) the preferred way is to use annotations. You can check the annotations documentation here.
As a side note, NUnit, reuses the instance of the test, so in the same scenario, it would only instantiate MyTest once.
JUnit will instantiate this class once per test method, so only once in the code above, but try it again with two test methods and you will see it instantiated twice. If you want to save some state in fields without having to use statics, take a look at TestNG, which reuses the same instance for all test methods.