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

问题描述:

为了测试目的,我无法更改字段的值。我可以修改字段的值以进行测试吗?

尽管单身人士问题我想用它来做我的日志文件管理器。我实际上给了它一个想法,如果我应该重做我的设计,但很高兴我的决定可能是好的approved

我想测试它。特别是如果代码在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 language docs

What's the proper way to test a class with private methods using JUnit?

Can I discover a Java class' declared inner classes using reflection?

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.dirname设置为任何值,然后再使用LogsMngr.Holder即可。

+0

为什么初始化后静态字段不可更改,它没有标记为“final”? – panny 2013-03-12 05:45:41

+0

这是可以改变的,但是你的单例是在初始化时创建的,所以之后的任何改变都不会产生任何影响。 – 2013-03-13 01:38:31