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