简体   繁体   中英

Instantiating a Class with private constructor using Java/Mockito/PowerMockito

I am writing a test case using JUnit and the method under test takes a final class with a private constructor as a parameter. Since I cannot instantiate it with the new keyword I tried using Mockito but found out that Mockito doesn't like final class . I went to use PowerMockito which seemed reasonable to me but PowerMockito.mockStatic(Field.class); is a void method and I need a reference of Field so that I can pass it as an argument while invoking the method.

I want to catch IllegalArgumentException but first I need to pass reference of Field as an argument

Method under test

public boolean accept(Field field) { 
    if( ignoreNulls ) {
        try {
            if( field.get( super.getObject() ) == null ) {
                return false;
            }
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }
    }

    return super.accept(field); 
} 

JUnit test case

   @Test(expected=IllegalArgumentException.class)
    public void testAccept() throws Exception {
      DefaultToStringBuilder builder = new DefaultToStringBuilder(new Object());
      PowerMockito.mockStatic(Field.class);

      builder.accept(?);
}

I am not sure how should I be doing this.

Thanks in advance

We can actually use Core Java to achieve this. Code below shows how to do it.

    private Field field;

    @Test(expected=IllegalArgumentException.class)
    public void testAccept() throws Exception {
      Class<?> clazz = Field.class;
      Constructor<?> [] constructors = clazz.getDeclaredConstructors();

      for(Constructor cons: constructors) {
          cons.setAccessible(true);
          field = (Field) cons.newInstance();
      }

      DefaultToStringBuilder builder = new DefaultToStringBuilder(new Object());
      builder.accept(field);

      assertNotNull(builder);
    }

My answer don't do that . Do not engage in PowerMock just because your production code can't be tested otherwise.

You will find out soon that PowerMock can create more problems than it solves.

Typically, the need to use PowerMock comes from a broken design. So, instead of spending hours to enable a broken design for testing via PowerMock ... you better spent a fraction of that time in order to rework your design. (and from my one experience: PowerMock can quickly lead to countless hours being spent on it)

Meaning: you could add a package protected constructor for testing purposes. Or you might go one step further to get the broader picture; and find ways that allow for a public constructor; whilst maintaining the design ideas that lead to the current final/private implementation.

Use java reflection to get object for private construtor from outside class. Here is example.

//Sample class Student.java

 public class Student {
        private Integer sudentId;
        private String studentName;
        private Student(){}
        private Student(Integer studentId, String studentName) {
            this.studentId = studentId;
            this.studentName = studentName;
        }
        public Integer getStudentId() {
            return studentId;
        }
        public String getStudentName() {
            return studentName;
        }
    }

In below code, There are two ways to instantiate the class
1- Find the private constructor using given constructor name and instantiate the class. 2- Find the private constructor for given number of arguments and types and instantiate the class

        import java.lang.reflect.Constructor;
        import java.lang.reflect.InvocationTargetException;
        import java.lang.reflect.Modifier;

        public class PrivateConstructorDemo {
            //Find the private constructor using given constructor name and instantiate the class.
            public void createObjectByConstructorName(int id, String name) throws NoSuchMethodException, SecurityException,
                    InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{

                Constructor<Student> constructor = Student.class.getDeclaredConstructor(Integer.class, String.class);
                if (Modifier.isPrivate(constructor.getModifiers())) {
                    constructor.setAccessible(true);
                    Student student = (Student)constructor.newInstance(id, name);
                    System.out.println("Student Id:"+ student.getStudentId());
                    System.out.println("Student Name:"+ student.getStudentName());
                }
            } 

            //For given number of arguments and types and instantiate the class. 
            public void createObject(int id, String name) throws InstantiationException, 
                                IllegalAccessException, IllegalArgumentException, InvocationTargetException {

                   Constructor<?>[] constructors = Student.class.getDeclaredConstructors();
                   for (Constructor<?> constructor : constructors) {
                     if (Modifier.isPrivate(constructor.getModifiers())) {
                        constructor.setAccessible(true);
                        Class<?>[] clazzs = constructor.getParameterTypes();
                        if (constructor.getParameterCount() == 2 && clazzs[0] == Integer.class && 
                                                             clazzs[1]  == String.class) {
                            Object ob = constructor.newInstance(id, name);
                            if (ob instanceof Student) {
                                Student student = (Student)ob;
                                System.out.println("Student Id:"+ student.getStudentId());
                                System.out.println("Student Name:"+ student.getStudentName());
                            }
                        }
                     }
                   }
            }

            public static void main(String[] args) throws InstantiationException, IllegalAccessException,
                    IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {

                PrivateConstructorDemo obj = new PrivateConstructorDemo();
                obj.createObject(10, "Sandeep");
                System.out.println("-------------------------");
                obj.createObjectByConstructorName(20,"Sandeep");
            }
        } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM