For software developers, unit testing is an essential task. It not only verifies the effectiveness of the program, but also helps developers quickly locate the problem when the program encounters a BUG. However, in the process of writing unit tests, developers often need some non-public member variables or methods of the attention Class, which brings great troubles to the test work. This article summarizes the four methods for non-public member variables or methods of the primary class, so that the tester can select when the primary class non-public member variables or methods are required.
Although many experienced programmers believe that they should not advocate private member variables or methods of the Rule class, this violates the basic rules of Java language encapsulation. However, the objects tested in actual tests are amazing. In order to perform unit tests effectively and quickly, sometimes we have to violate some of these or other rules. This article only discusses how to use non-public member variables or methods of the category class. As to whether this should be done in development and testing, the readers will be left to judge and choose based on the actual situation.
Method 1: Modify the access modifier
The simplest and most direct method is to use the features of Java to access non-public members. To put it bluntly, you can directly change the private and protected keywords to public or delete them directly. We recommend that you delete it directly, because the default access modifier is visible in the Java language definition. After doing so, we can create another source code directory-test directory (most ides support this, such as Eclipse and JBuilder), and then put the test class in the same package as the test directory, to access the member variables and methods of the class to be tested. At this time, the code of other packages still cannot access these variables or methods, ensuring the encapsulation of the program to a certain extent.
The following sample code shows this method.
Listing 1. Original class A code to be tested
Public class A {private String name = null; private void calculate (){}}
Listing 2. Code of Class A to be tested after unit test Modification
Public class A {String name = null; private void calculate (){}}
Although this method looks simple and crude, experience tells us that this method is very effective during testing. Of course, because the source code is changed, although it is only visible in the package, it has damaged the encapsulation of the object. For most systems that require strict code security, this method is not desirable.
Method 2: Use the security manager
The combination of security manager and reflection mechanism can also achieve our goal. Java runtime relies on a security manager to check whether the calling code has sufficient permissions for a specific access. Specifically, the security manager is a java. lang. SecurityManager class or a class that extends from this class, And it checks the operation permissions of some applications at runtime. In other words, all object access must be delegated to the security manager before executing its own logic. When access is controlled by the security manager, the application can only perform operations that are specifically permitted by the relevant security policy. Therefore, once started, the security manager can provide sufficient protection for the code. By default, the security manager is not set. Unless the Code explicitly installs a default or custom security manager, the access control check during runtime does not work. We can avoid Java access control check during runtime to achieve our access to non-public member variables or methods. To access the Non-Public members we need, we also need to use Java reflection technology. Java reflection is a powerful tool that allows us to configure Code while running without having to link source code between objects, making code more flexible. During compilation, the Java compiler ensures the private properties of private members, so that the private methods and private member variables of a class cannot be statically referenced by other classes. However, the Java reflection mechanism allows us to query and access variables and methods at runtime. Because reflection is dynamic, the check at compilation will no longer work.
The following code demonstrates how to use the security manager and reflection mechanism to access private variables.
Listing 3. Using the reflection mechanism, member variables of the callback class
● Field [] getDeclaredFields (): returns an array of all member variables declared by the loaded class, excluding the member variables inherited from the parent class.
● Field getDeclaredField (String name): return the Field object of all member variables declared by the loaded class, excluding member variables inherited from the parent class. The parameter name specifies the name of the member variable.
● Field [] getFields (): returns the Field object array of all public member variables declared by the loaded class, including member variables inherited from the parent class.
● Field getField (String name): returns the Field object of all member variables declared by the loaded class, including member variables inherited from the parent class. The parameter name specifies the name of the member variable.
// Obtain the value of the specified variable
Public static Object getValue (Object instance, String fieldName)
Throws IllegalAccessException, NoSuchFieldException ...{
Field field = getField (instance. getClass (), fieldName );
// If the parameter value is true, the access control check is disabled.
Field. setAccessible (true );
Return field. get (instance );
}
// This method obtains the value of this variable based on the variable name.
Public static Field getField (Class thisClass, String fieldName)
Throws NoSuchFieldException ...{
If (thisClass = null )...{
Throw new NoSuchFieldException ("Error field! ");
}
}
GetField (instance. getClass (), fieldName) obtains object attributes through the reflection mechanism. You can reset the value of a variable by using the set method, such as field. set (instance, newValue );. If a security manager exists, the method first uses this and Member. DECLARED as parameters to call the checkMemberAccess method of the security manager. here this is the parent class of this class or the Member is determined. If the class is in the package, the method also uses the package name as the parameter to call the checkPackageAccess method of the security manager. Every call may cause SecurityException. When the access is denied, both call methods generate a securityexception.
The setAccessible (true) method disables the access control check by setting the parameter value to true, so that the variable can be called by other classes. We can extend a common basic class java. lang. reflect. AccessibleObject in the class we write. This class defines a setAccessible method that enables or disables access detection for one of these classes. The problem with this method is that if the security manager is used, it will detect whether the code that is disabling access detection allows this. If not, the security manager throws an exception.
In addition to accessing private variables, we can also use this method to access private methods.
Listing 4. Using the reflection mechanism the member method of the callback class
● Method [] getDeclaredMethods (): returns an array of Method objects for all methods declared by the loaded class, excluding methods inherited from the parent class.
● Method getDeclaredMethod (String name, Class [] paramTypes): return the Method object of all methods declared by the loaded Class, excluding methods inherited from the parent Class. The parameter name specifies the name of the Method, the parameter paramTypes specifies the parameter type of the method.
● Method [] getMethods (): returns an array of Method objects of all methods declared by the loaded class, including methods inherited from the parent class.
● Method getMethod (String name, Class [] paramTypes): return the Method object of all methods declared by the loaded Class, including methods inherited from the parent Class. The parameter name specifies the name of the Method, the parameter paramTypes specifies the parameter type of the method.
Public static Method getMethod (Object instance, String methodName, Class [] classTypes)
Throws NoSuchMethodException ...{
Method accessMethod = getMethod (instance. getClass (), methodName, classTypes );
// If the parameter value is true, the access control check is disabled.
AccessMethod. setAccessible (true );
Return accessMethod;
}
Private static Method getMethod (Class thisClass, String methodName, Class [] classTypes)
Throws NoSuchMethodException ...{
If (thisClass = null )...{
Throw new NoSuchMethodException ("Error method! ");
} Try ...{
Return thisClass. getDeclaredMethod (methodName, classTypes );
} Catch (NoSuchMethodException e )...{
Return getMethod (thisClass. getSuperclass (), methodName, classTypes );
}
}
The principle of obtaining a private method is the same as that of obtaining a private variable. When we get the function, we need to call it. In this case, we need to use the invoke () method to call the function. The code example is as follows:
// Call a method containing a single parameter
Public static Object invokeMethod (Object instance, String methodName, Object arg)
Throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException ...{
Object [] args = new Object [1];
Args [0] = arg;
Return invokeMethod (instance, methodName, args );
}
// Call a method containing multiple parameters
Public static Object invokeMethod (Object instance, String methodName, Object [] args)
Throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException ...{
Class [] classTypes = null;
& N