0

我在更改字段值以进行测试时遇到问题。

尽管单例存在问题,但我想将它用于我的日志文件管理器。我实际上考虑过是否应该重新设计我的设计,但很高兴的决定可能是好的。

不过我想测试一下。特别是如果代码在 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();
        }
    }
}

参考:

单独的ClassLoaderRunner.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();
        }
    }
}
4

1 回答 1

1

这一行:

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

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

要完成您想要完成的工作,您必须能够在dirname不首先初始化包含它的类的情况下设置该字段,我认为这在 Java 中是不可能的,尽管我有兴趣在这一点上得到纠正。但是,我相信您只需dirname移出Holder和移入LogsMngr. 然后LogsMngr.dirname在使用任何东西之前简单地设置为你想要的任何值LogsMngr.Holder

于 2013-03-12T03:23:54.457 回答