使用 Java/Mockito/PowerMockito 实例化具有私有构造函数的类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37447400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:30:57  来源:igfitidea点击:

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

javajunitmockitopowermockito

提问by GhostCat

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

我正在编写一个测试用例,被测JUnit方法采用一个带有私有构造函数作为参数的最终类。由于我无法new使用我尝试使用的关键字实例化它,Mockito但发现Mockito它不喜欢final class. 我去使用PowerMockito这对我来说似乎合理但它PowerMockito.mockStatic(Field.class);是一个无效的方法,我需要一个引用,Field以便我可以在调用该方法时将其作为参数传递。

I want to catch IllegalArgumentExceptionbut first I need to pass reference of Fieldas an argument

我想赶上,IllegalArgumentException但首先我需要将引用Field作为参数传递

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

JUnit 测试用例

   @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

提前致谢

回答by GhostCat

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

我的回答不会那样做。不要仅仅因为您的生产代码无法通过其他方式进行测试而参与 PowerMock。

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

您很快就会发现 PowerMock 会产生比它解决的更多的问题。

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)

通常情况下,需要使用 PowerMock 来自一个损坏的设计。因此,与其花费数小时通过 PowerMock 启用损坏的设计以进行测试……您最好花一小部分时间来重新设计您的设计。(根据我的一次经验:PowerMock 很快就会导致在它上面花费无数个小时)

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.

意思是:您可以添加一个包保护的构造函数以进行测试。或者,您可能会更进一步以了解更广泛的情况;并找到允许公共构造函数的方法;同时保持导致当前最终/私人实施的设计理念。

回答by GhostCat

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

我们实际上可以使用它Core Java来实现这一点。下面的代码显示了如何做到这一点。

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

回答by Sandeep Srivastava

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

使用 java 反射从外部类获取私有构造函数的对象。这是示例。

//Sample class Student.java

//示例类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

在下面的代码中,有两种方法可以实例化类
1-使用给定的构造函数名称查找私有构造函数并实例化类。2- 找到给定数量的参数和类型的私有构造函数并实例化类

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