java类的测试( main函数)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java类的测试( main函数)相关的知识,希望对你有一定的参考价值。

《java编程思想》上面说“我曾在本书中建议过,在每个类中都写入一个main方法,用来测试这个类,这样做有一个缺点,那就是必须带着那些已编译过的额外代码。如果这对你是一个麻烦,那就可以使用嵌套类来放置测试代码”,我想问一下,“测试类时带着那些已编译过的额外代码”是什么意思??嵌套类又是怎样克服这个缺点的??也就是说,测试类时的具体过程是怎样的?

额外代码就是代表你main函数里面的测试代码,那些代码对业务并没有任何用处,但是它却被编译进了你的class文件里面。
嵌套类就其实另外一个类,但是因为它嵌套在外层的类里,所以能拥有访问外层类所有成员的权限(包括private),但是嵌套类在虚拟机层面是一个独立的类,名称是“OuterClass$NestedClass”,因为没有其它类能依赖于这个类(因为NestedClass是OuterClass的私有成员),所以生成的这个class文件能从jar包中移除。所以说嵌套类能克服这个缺点。
参考技术A 用某些IDE(比如eclipse,netbeans等)执行编写的Java程序时,经常会跳出一个对话框,要求选择使用那个类来运行。也就是说在Java程序中有一个主类,整个程序的入口就这这个主类中的main函数。非主类中也可以有主函数,所有在某些情况下,我们可以用这些主函数来测试某些模块的功能,在python中也是这样。
书中说这会带来额外的代码,其实这很好理解,当各个模块综合到一起后,那些作为测试用的main函数正常情况下都是不被执行的(除非是在测试),那么程序写好后,他们就是额外的代码了。

所谓的嵌套类,应该是指类的组合或者是继承吧。
外层类可以调用底层类的main函数来进行测试,这样就避免了额外的代码。测试内层类时,调用内层类的main函数,测试外层类时,调用外层类的mian函数。
参考技术B 现在都用JUnit了,具体方法就是,在你要测试的类的定义的前一行,写一个@Test,然后 run as junit test 就okay了 当然junit还有很多东西,大致就是这个用法

Java:没有默认构造函数的类的newInstance

【中文标题】Java:没有默认构造函数的类的newInstance【英文标题】:Java: newInstance of class that has no default constructor 【发布时间】:2011-04-09 22:56:58 【问题描述】:

我正在尝试为学生的家庭作业构建一个自动测试框架(基于 jUnit,但这并不重要)。他们必须为某些类创建构造函数,并为它们添加一些方法。稍后,通过我提供的测试功能,他们会检查是否正常。

我想要做的是,通过反射,为我想要测试的某个类创建一个新实例。问题在于,有时没有默认构造函数。我不在乎,我想自己创建一个实例并初始化实例变量。有没有办法做到这一点? 很抱歉,如果以前有人问过这个问题,但我找不到任何答案。

提前致谢。

【问题讨论】:

【参考方案1】:

调用Class.getConstructor() 然后Constructor.newInstance() 传入适当的参数。示例代码:

import java.lang.reflect.*;

public class Test 

    public Test(int x) 
        System.out.println("Constuctor called! x = " + x);
    

    // Don't just declare "throws Exception" in real code!
    public static void main(String[] args) throws Exception 
        Class<Test> clazz = Test.class;
        Constructor<Test> ctor = clazz.getConstructor(int.class);
        Test instance = ctor.newInstance(5);           
    

【讨论】:

这将涉及一些混乱的反射来获取一个构造函数,并遍历它,为每个参数提供一个适当的值...... 谢谢。问题是我不知道他们是否已经添加了构造函数。当然,我可以通过捕获适当的异常来检查他们是否这样做。但我不知道他们是否使用正确的参数创建了构造函数。更糟糕的是,我不知道构造函数是否工作正常。我想构建实例而不依赖于它们的实现。 @GermanK:然后改用 Class.getConstructors() 看看有什么可用的。你必须依赖一个实现来实例化一个类。如果您创建一个实例而不使用适当的参数调用它们的构造函数之一,那么您对它们的类​​不公平,它们会期望被正确实例化。我建议你授权一个特定的签名。 @GermanK 然后在你的测试方法的末尾有一个可变参数,用户输入必要的参数 @GermanK,你是教授。如果学生没有正确完成作业,则学生失败。向他们反馈他们失败的原因。下一次,他们会更加小心。【参考方案2】:

这是一个不需要 javassist 或其他字节码“操纵器”的通用解决方案。虽然,它假设构造函数除了简单地将参数分配给相应的字段之外没有做任何其他事情,所以它只是选择第一个构造函数并创建一个具有默认值的实例(即,0 代表 int,null 代表 Object 等)。

private <T> T instantiate(Class<T> cls, Map<String, ? extends Object> args) throws Exception

    // Create instance of the given class
    final Constructor<T> constr = (Constructor<T>) cls.getConstructors()[0];
    final List<Object> params = new ArrayList<Object>();
    for (Class<?> pType : constr.getParameterTypes())
    
        params.add((pType.isPrimitive()) ? ClassUtils.primitiveToWrapper(pType).newInstance() : null);
    
    final T instance = constr.newInstance(params.toArray());

    // Set separate fields
    for (Map.Entry<String, ? extends Object> arg : args.entrySet()) 
        Field f = cls.getDeclaredField(arg.getKey());
        f.setAccessible(true);
        f.set(instance, arg.getValue());
    

    return instance;

附:适用于 Java 1.5+。该解决方案还假设没有可以阻止调用 f.setAccessible(true) 的 SecurityManager 管理器。

【讨论】:

这很好,但我认为应该是:params.add((pType.isPrimitive()) ? 0 : null); @NT_ 好地方。虽然简单地传递零是行不通的,但需要正确的类型。 newInstance() 将在将 pType 转换为包装类后起作用(例如,可以使用来自 apache-commons 的 ClassUtils)。 呃,你什么意思?它似乎对我有用。编译器将执行必要的缩小/扩展和装箱,0 将转换为所有原语的默认值。我已经使用了很长一段时间没有问题... 编译器无法捕捉到它,因为 pType 的真实类型仅在运行时才知道,而构造函数参数类型匹配是在运行时完成的。可能您将它与兼容的类型(例如 int)一起使用,尝试使用“char”类型的字段。【参考方案3】:

如果您没有使用过模拟框架(例如 ezmock),我强烈建议您尝试一下。

我可能是错的,这可能对你没有任何帮助,但从我从你的帖子中收集到的信息来看,嘲笑可能正是你正在寻找的(尽管我承认它与你要求什么。

编辑:回应评论。

不,现代模拟框架允许您从“无”创建任何类的“假”实例,并将其作为类的实例传递。它不需要接口,它可以是任何类。还可以编写方法以返回一系列值,从简单的始终返回“7”到“使用 arg=7 调用时,第一次调用返回 5,第二次调用返回 6,第三次调用返回 7”。

它通常与测试框架结合使用,以提供参考类以传递给您正在测试的类。

这可能不是您想要的,但您提到了单元测试和手动初始化变量,所以看起来这最终可能会派上用场。

【讨论】:

我认为这需要一些模拟框架将实现的接口,对吧?因为我没有接口......它们是非常简单的类,学生将实现这些类。【参考方案4】:

我使用以下代码创建传入的任何类型的类名的通用对象列表。它使用类中的所有 set 方法来设置通过结果集传入的所有值。如果有人也对它感兴趣,我会发布这个。

protected List<Object> FillObject(ResultSet rs, String className)
    
        List<Object> dList = new ArrayList<Object>();

        try
        
            ClassLoader classLoader = GenericModel.class.getClassLoader();

            while (rs.next())
            
                Class reflectionClass = classLoader.loadClass("models." + className);

                Object objectClass = reflectionClass.newInstance();

                Method[] methods = reflectionClass.getMethods();

                for(Method method: methods)
                
                    if (method.getName().indexOf("set") > -1)
                    
                        Class[] parameterTypes = method.getParameterTypes();

                        for(Class pT: parameterTypes)
                        
                            Method setMethod = reflectionClass.getMethod(method.getName(), pT);

                            switch(pT.getName())
                            
                                case "int":
                                    int intValue = rs.getInt(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, intValue);
                                    break;

                                case "java.util.Date":
                                    Date dateValue = rs.getDate(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, dateValue);
                                    break;

                                case "boolean":
                                    boolean boolValue = rs.getBoolean(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, boolValue);
                                    break;

                                default:
                                    String stringValue = rs.getString(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, stringValue);
                                    break;
                            
                        
                    
                

                dList.add(objectClass);
            
        
        catch (Exception e)
        
            this.setConnectionMessage("ERROR: reflection class loading: " + e.getMessage());
        

        return dList;
    

【讨论】:

【参考方案5】:

您可以使用Class.getConstructor或Class.getConstructors,然后使用Constructor.newInstance方法来初始化您要使用的对象。

【讨论】:

使用没有参数的 Class.getConstructor 或 Class.getDeclaredConstructor 如果没有声明默认构造函数,则会得到 java.lang.NoSuchMethodException @GermanK 如果是这样的话,我想知道你为什么接受这个答案。如果你没有用int 参数类型声明构造函数,它会抛出同样的异常 @Farid 我猜区别在于参数,但谁记得 9 年后:)【参考方案6】:

您可以随作业分发以下源代码。告诉学生将其包含在他们的源代码中。他们的代码不会编译,除非他们用正确的签名编写一个 Assignment 类。编译器会为您进行签名检查。

那么你的测试程序就不需要使用反射了。只需实例化一个 AssignmentFactory 并使用适当的参数调用 make 方法。

如果你使用这个想法,你的新挑战将是一些学生修改 AssignmentFactory 以适应他们的 Assignment 课程(破坏你的测试程序)。

package assignment ;

public class AssignmentFactory

     public AssignmentFactory ( )
     
           super ( ) ;
     

     public AssignmentFactory make ( .... parameters )
     
           return new Assignment ( .... arguments ) ;
     

【讨论】:

这只是测试(签名正确性)编译时间的一部分......如果他们没有正确初始化实例变量会发生什么?无论如何,我仍然需要测试它们。另一方面,我不想添加任何会分散他们在任务中主要目标的内容。 是的,您仍然需要评估他们的作业。 AssignmentFactory 的目的是试图强制他们以适合编程评估的格式提交作业。

以上是关于java类的测试( main函数)的主要内容,如果未能解决你的问题,请参考以下文章

vs2010中写c#,写了很多类,想在类里面添加main()做函数入口测试,但不能单独运行这个类

JAVA里面的main函数为啥要定义为static的?

JAVA中如果有多个类,那么main函数要要放到哪一个类里面?任何一个类都可以吗?

程序的入口

java基础-main方法详解

Android Studio编写运行测试纯java代码可带main()函数