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

白昼怎懂夜的黑 提交于 2020-08-07 05:47:34

问题


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


回答1:


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.




回答2:


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);
    }



回答3:


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


来源:https://stackoverflow.com/questions/37447400/instantiating-a-class-with-private-constructor-using-java-mockito-powermockito

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!