[Java] Refer to and set private variables with reflection

Refer to and set private variables

Earlier I wrote an article Testing private methods in JUnit. In short, you can also test ** private methods directly from JUnit! So please don't be foolish to make any access modifier public or not in the first place for JUnit! It's because of the claim that **, but it's like a derivative of it.

It's not a decoration. The access modifier is Hahan. Think about the meaning and attach it.

It's shit code that the intent of access modifiers seems to be only for testing, and it's just ** harmful **. It becomes necessary to judge that the code and design document of such a project are unreliable and to doubt everything, and the actual man-hours will increase from the rough estimate. (Experience story)

If you use a mock library, you can write more easily without using reflection (such as Mockito's Whitebox), but ** If you hit a project that does not allow the introduction of a mock library, there are people who carelessly remove the access modifier **. , I wrote this article. Stop! please stop! Stop writing the wrong fucking code for testing! really! !!


Sample code and examples

Set / reference the private variable of the class to be tested from another JUnit class. The sample code omits Javadoc and some kettles. I have confirmed the operation so that it works with copy and paste, but it is just a sample code and reference.

Class to be tested

The constructor is omitted. Basically it just has a private variable and a getter that references the private variable.

package test.variable;

import java.util.Objects;

public class Sample {
	
 / ** 1. private instance variable * /
	private String strValue = null;

 / ** 2. private class variable * /
	private static String strValueStatic = null;

	private InnerDynamic innerDynamic = null;

	private static InnerStatic innerStatic = new InnerStatic();

	public String getStrValue() {
		return this.strValue;
	}

	public static String getStrValueStatic() {
		return strValueStatic;
	}
	
	public String getDynamicInnerPrivate() {
		if (Objects.isNull(this.innerDynamic)) {
			this.innerDynamic = new InnerDynamic();
		}
		return this.innerDynamic.getIStrValue();
	}

	public static String getStaticInnerPrivate() {
		return innerStatic.getIStrValue();
	}

	private class InnerDynamic {
 / ** 3. Instance variables of dynamic private class * /
		private String iStrValue = null;

		public String getIStrValue() {
			return this.iStrValue;
		}
	}

	private static class InnerStatic {
 / ** 4. Instance variables of static private class * /
		private String iStrValue = null;

		public String getIStrValue() {
			return this.iStrValue;
		}
	}
}

1. Private instance variable setting / reference

The type that requires an instance because it is not static. Allows you to reference and set private variables for a particular instance.

package test.variable;

import static org.junit.Assert.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import org.junit.Test;

public class SampleTest {
 / ** 1. private instance variable * /
	@Test
	public void test_private_field() throws Exception {
		String testValue = "test";
		Sample testee = new Sample();
 // Check the initial value
		assertNull(testee.getStrValue());

 // Get the field of private variable
		Field field = testee.getClass().getDeclaredField("strValue");
 // Remove access restrictions on private variables
		field.setAccessible(true);
 // Set a value for a private variable
		field.set(testee, testValue);

 // Check private variables
		String value = String.valueOf(field.get(testee));
		assertEquals(testValue, value);
		assertEquals(value, testee.getStrValue());
	}
}

2. Private class variable setting / reference

In the case of static, the contents of a specific instance are not changed, so the class object is not obtained from the instance, or the first argument is set to null when setting the value.

package test.variable;

import static org.junit.Assert.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import org.junit.Test;

public class SampleTest {
 / ** 2. private class variable * /
	@Test
	public void test_private_static_field() throws Exception {
		String testValue = "test";
 // Check the initial value
		assertNull(Sample.getStrValueStatic());

 // Get the field of private variable
		Field field = Sample.class.getDeclaredField("strValueStatic");
 // Remove access restrictions on private variables
		field.setAccessible(true);
 // Set a value for a private variable
		field.set(null, testValue);
		
 // Check private variables
		String value = String.valueOf(field.get(null));
		assertEquals(testValue, value);
		assertEquals(value, Sample.getStrValueStatic());
	}
}

3. Setting / reference of instance variables of dynamic private class

In the case of the inner class, it is invisible to the outer class, so we get the constructor via the class loader. To get it, specify the class $ inner class outside the fully qualified name. After getting the inner class object, get the field as above and remove the access restriction of the field.

package test.variable;

import static org.junit.Assert.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import org.junit.Test;

public class SampleTest {
 / ** 3. Instance variables of dynamic private class * /
	@Test
	public void test_dynamic_inner_class_private_field() throws Exception {
		String testValue = "test";
		Sample testee = new Sample();
 // Check the initial value
		assertNull(testee.getDynamicInnerPrivate());

 // Get the inner class object
		Class innerClazz = Class.forName("test.variable.Sample$InnerDynamic");
		Constructor constructor = innerClazz.getDeclaredConstructor(new Class[]{Sample.class});
		constructor.setAccessible(true);
		Object inner = constructor.newInstance(testee);
 // Get the field of the private variable of the inner class
		Field field = inner.getClass().getDeclaredField("iStrValue");
		field.setAccessible(true);
		field.set(inner, testValue);
 // Get the field that holds the inner class of the outer class
		Field outerFld = testee.getClass().getDeclaredField("innerDynamic");
		outerFld.setAccessible(true);
		outerFld.set(testee, inner);
		
 // Check private variables
		String value = String.valueOf(field.get(inner));
		assertEquals(testValue, value);
		assertEquals(value, testee.getDynamicInnerPrivate());
	}

If the constructor takes arguments

If the constructor of the inner class has arguments, the first argument will be the outer class or the object, so the second and subsequent arguments will be the arguments of the inner class constructor.

 // Get the constructor
Constructor constructor = 
		innerClazz.getDeclaredConstructor(new Class[]{Sample.class, String.class});

 // Get the object
Object inner = constructor.newInstance(testee, "constructor");

4. Setting / reference of instance variables of static private class

As with the dynamic inner class, first get the constructor. To get it, specify the class $ inner class outside the fully qualified name. The difference between the static and dynamic inner classes is that the outer class is specified as the first argument passed at the time of acquisition.

package test.variable;

import static org.junit.Assert.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import org.junit.Test;

public class SampleTest {
 / ** 4. Instance variables of static private class * /
	@Test
	public void test_static_inner_class_private_field() throws Exception {
		String testValue = "test";
 // Check the initial value
		assertNull(Sample.getStaticInnerPrivate());

 // Get the inner class object
		Class innerClazz = Class.forName("test.variable.Sample$InnerStatic");
		Constructor constructor = innerClazz.getDeclaredConstructor();
		constructor.setAccessible(true);
		Object inner = constructor.newInstance();
 // Get the field of the private variable of the inner class
		Field field = inner.getClass().getDeclaredField("iStrValue");
		field.setAccessible(true);
		field.set(inner, testValue);
 // Get the field that holds the inner class of the outer class
		Field outerFld = Sample.class.getDeclaredField("innerStatic");
		outerFld.setAccessible(true);
		outerFld.set(null, inner);
		
 // Check private variables
		String value = String.valueOf(field.get(inner));
		assertEquals(testValue, value);
		assertEquals(value, Sample.getStaticInnerPrivate());
	}
}

If the constructor takes arguments

The example calls a constructor with no arguments, but in the case of a constructor like ʻInnerStatic (String str)`, specify the argument type and value at the time of acquisition.

 // Get the constructor
Constructor constructor = innerClazz.getDeclaredConstructor(new Class[]{String.class});

 // Get the object
Object inner = constructor.newInstance("constructor");

Finally

Well, because it's a test, I use reflection to call invisible fields and methods like this, but just because I can call it, I think it's a good idea to use reflection on the person being tested. I think that it is rare to use reflection in the source of the program itself, so if you use it, it may be better to review it from the design. (Except if you are making the framework itself) If you're not sure, talk to someone who might be familiar with you. Especially if you are in an environment where code reviews are not done properly.

bonus No, I'm in the process of changing jobs, but I'm looking for a workplace where I can write my own code (even while giving technical guidance to newcomers). I'm the type of person who isn't suitable for management, so please contact me if you have a good workplace to write code.

Recommended Posts

[Java] Refer to and set private variables with reflection
Try to link Ruby and Java with Dapr
How to access Java Private methods and fields
[Java] Variables and types
How to call functions in bulk with Java reflection
Excel sheet and set Tab colors to rename Java
I want to transition screens with kotlin and java!
Java to play with Function
Java programming (variables and data)
How to set Java constants
Connect to DB with Java
Connect to MySQL 8 with Java
How to use Java variables
How to set environment variables when using Payjp with Rails
I want to make a list with kotlin and java!
I want to make a function with kotlin and java!
How to set and describe environment variables using Rails zsh
Sample code to parse date and time with Java SimpleDateFormat
I want to implement various functions with kotlin and java!
Java to learn with ramen [Part 1]
Use java with MSYS and Cygwin
Distributed tracing with OpenCensus and Java
Personal memo: Metaprogramming with Java reflection
[Java] Points to note with Arrays.asList ()
Java Primer Series (Variables and Types)
[Introduction to Java] Variables and types (variable declaration, initialization, data type)
I want to return to the previous screen with kotlin and java!
Basics of Java development ~ How to write programs (variables and types) ~
Dare to challenge Kaggle with Java (1)
Use JDBC with Java and Scala.
[Processing × Java] How to use variables
I tried to interact with Java
Output PDF and TIFF with Java 8
Look through Java and MySQL PATH with environment variables [Windows version]
[Java] Test private methods with JUnit
How to name variables in Java
Java, arrays to start with beginners
Encrypt with Java and decrypt with C #
How to encrypt and decrypt with RSA public key in Java
How to test a private method in Java and partially mock that method
I want to display images with REST Controller of Java and Spring!
[Node.js express Docker] How to define Docker environment variables and load them with node.js
How to make an app with a plugin mechanism [C # and Java]
Java8 / 9 Beginners: Stream API addiction points and how to deal with them
Java starting from beginner, variables and types
How to compile Java with VsCode & Ant
Monitor Java applications with jolokia and hawtio
Link Java and C ++ code with SWIG
Let's try WebSocket with Java and javascript!
Introduction to algorithms with java --Search (depth-first search)
Java implementation to create and solve mazes
[Java] Reading and writing files with OpenCSV
[Java] Differences between instance variables and class variables
[Java] How to output and write files!
How to set up and use kapt
[Java] How to set the Date time to 00:00:00
How to set JAVA_HOME with Maven appassembler-maven-plugin
Force non-instantiation with Effective Java private constructor
Java programming (static clauses and "class variables")
[Introduction to Java] Variable declarations and types
How to test private scope with JUnit