我可以修改字段的值以进行测试吗?



我在为测试目的更改字段值时遇到了麻烦。

尽管存在围绕单例的问题,但我想将其用于我的日志文件管理器。我确实考虑过是否应该重新设计,但很高兴我的决定得到了认可。

我想测试一下。特别是如果代码在windows和linux上运行,或者在其他我现在想不起来的地方。

这个想法是在当前目录下创建一个名为"testlogs"的测试目录,删除它并使用它。因此,我可以确保我至少在当前目录中具有写权限,或者我可以通知用户不写入日志文件,或者我可以提示他选择另一个位置等。

在下面的代码中,我一直得到我的"日志"目录。我希望能够省去真正的"日志"(如果它们存在的话)。

为什么我不能改变dirname的值

package com.home.app.logger.LogsMngr;
public class LogsMngr {
    private static class Holder {
        public static String dirname = "logs";
        public static final LogsMngr INSTANCE = new LogsMngr(dirname);
    }
    private LogsMngr(String dirname){
         createDirectory(dirname);
    }
    private createDirectory(String dirname) {
         // logic to create a directory on the filesystem
    }
    public static LogsMngr getInstance() {
        return Holder.INSTANCE;
    }
}

@RunWith(SeparateClassLoaderRunner.class)
public class Test {
    LogsMngr lmngr;
    @Test
    public void test() {
        try {
            // Remove existing directory
            TUtils.rmfDirectory("testlogs");
            // Set directory name
            String className = "com.home.app.logger.LogsMngr$Holder";
            Class[] memberClasses = LogsMngr.class.getDeclaredClasses();
            Field dirnamefield = null;
            for(int i=0; i<memberClasses.length; i++) {
                if(memberClasses[i].getName().equals(className)) {
                    dirnamefield = memberClasses[i].getField("dirname");
                }
            }
            //TODO catch NullPointerException because of dirname?
            // To prevent from IllegalAccessException:
            dirnamefield.setAccessible(true);
            // Set test directory
            dirnamefield.set(null, "testlogs");
            // Create
            lmngr = LogsMngr.getInstance();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
}

引用:

SeparateClassLoaderRunner.class

Java语言文档

使用JUnit测试带有私有方法的类的正确方法是什么?

我可以发现一个Java类的内部类声明使用反射?

The_solution_of_Bill_Pugh

解决方案:

package com.home.app.logger.LogsMngr;
public class LogsMngr {
    private static String dirname = "logs"; <-- moved out of Holder
    private static class Holder {
        public static final LogsMngr INSTANCE = new LogsMngr(dirname);
    }
    private LogsMngr(String dirname){
         createDirectory(dirname);
    }
    private createDirectory(String dirname) {
         // logic to create a directory on the filesystem
    }
    public static LogsMngr getInstance() {
        return Holder.INSTANCE;
    }
}

@RunWith(SeparateClassLoaderRunner.class)
public class Test {
    LogsMngr lmngr;
    @Test
    public void test() {
        try {
            // Remove existing directory
            TUtils.rmfDirectory("testlogs");
            Field dirnamefield = LogsMngr.class.getDeclaredField("dirname");
            // To prevent from IllegalAccessException:
            dirnamefield.setAccessible(true);
            // Set test directory
            dirnamefield.set(null, "testlogs");
            // Create
            lmngr = LogsMngr.getInstance();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
}

这一行:

Class[] memberClasses = LogsMngr.class.getDeclaredClasses();

将导致LogsMngr.Holder被初始化,导致INSTANCE字段也被初始化,因此在此之后的任何时间更改dirname都没有效果。

要做你想要完成的事情,你必须能够设置dirname字段,而不首先初始化包含它的类,我认为这在Java中是不可能的,尽管我很想纠正这一点。然而,我相信你可以通过简单地将dirnameHolder移到LogsMngr来获得你想要的效果。然后,在使用LogsMngr.Holder之前,只需将LogsMngr.dirname设置为您想要的任何值。

相关内容

  • 没有找到相关文章

最新更新