13

我正在使用一个 3rd 方库,它基本上创建了一个输出目录,其中包含不同类型的文件和子目录。我希望能够编写单元测试来确认输出是否正确。

我希望能够将 lib 与 RAM 磁盘一起使用,这样库所做的任何事情都不会以任何方式触及实际的磁盘板。这个想法是使测试运行和清理速度非常快(丢弃 RAM 磁盘?)。

我可以使用的两个最突出的选项是Commons VFSJSR 203。前者对我没有用,因为我希望使用 java.io.* API 而不是 Commons VFS 类透明地工作。后者并没有削减它,因为我必须使用 JDK 6(它应该是 JDK 7 的一部分)而且我不知道它是否可以与 java.io.* 无缝工作(我不会赌它)。

还有其他解决方案,但我不能使用它们,原因与我不能使用 Commons VFS 相同。由于所讨论的库的复杂性,模拟是不可能的。

在我的 linux 机器上,我可以轻松地创建一个 RAM 驱动器并使用 java.io.* API,就像使用磁盘上的文件一样。问题是,我希望它是跨平台的,更具体地说,是让磁盘设置成为测试过程的一部分,而不是外部的。

那么,有没有办法在 Java 中注册一个可与标准 java.io.* API 一起使用的 RAM 驱动器?

4

3 回答 3

6

那么,有没有办法在 Java 中注册一个可与标准 java.io.* API 一起使用的 RAM 驱动器?

不适用于 Java 6 或更早版本的 JVM。Java 6 及更早版本不提供任何用于注册文件系统或文件系统类型的 SPI。因此,要实现应用程序可以像普通 FS 一样使用的 RAM FS,需要修改许多java.io.*类的行为。

我认为您能做的最好的事情是使用由主机操作系统实现的 RAM FS。您应该能够从 Java 访问它,就好像它是一个普通的文件系统一样。但是,I/O需要系统调用,因此它不会像 RAM 文件系统保存在 JVM 托管内存中那样快。

于 2010-12-13T11:37:05.487 回答
4

从理论上讲,斯蒂芬是对的。但我可以建议你一个技巧。您可以实现自己的 FileInputStream 和 FileOutputStream 并将它们放入 bootclasspath。例如,您的实现将实现 open()、read() 和 readBytes()(它们是常规 FileInputStream 中的本机方法。)

这是针对您的问题的纯 Java 解决方案。它的缺点是您必须在单独的 JVM 实例中运行测试。

于 2010-12-13T11:58:31.053 回答
2

您要克服的基本问题是原始java.ioAPI 根本不灵活(它们都引用具体类)。例如,您可以将不同功能放入其中的唯一方法java.io.File是扩展基类。

在设计类之后扩展类可能是糟糕的设计(只需查看Properties类)——这就是为什么您可能找不到这样做的库的原因。

没有什么能阻止您java.io.File自己扩展类,并将所有方法代理到,例如,FileObjectCommons VFS API 的一个。

编辑:但是,在这种方法下,有些事情可能会失败 - 例如,使用File带有 parent 的构造函数File

编辑2:好吧,我将从这样的事情开始:

public class VirtualFile extends java.io.File {
    public static VirtualFile fromFile(File file) {
        if (file instanceof VirtualFile) {
            return (VirtualFile) file;
        } else {
            FileSystemManager fsm = new DefaultFileSystemManager();
            return fsm.toFileObject(file);
        }
    }

    private final org.apache.commons.vfs.FileObject mProxyFileObject;


    public VirtualFile(FileObject proxy) {
        super("/tmp/xxxx"); // That part needs some work to be cross-platform.
                            // However, such a construction will completely
                            // destroy the expectations that other classes 
                            // have about what a File is.
        mProxyFileObject = proxy;
    }

    public VirtualFile(VirtualFile parent, String child) {
        this(parent.mProxyFileObject.resolveFile(child));
    }

    public VirtualFile(File parent, String child) {
        this(fromFile(parent), child);
    }

    @Override
    public boolean canExecute() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean canRead() {
        try {
            return mProxyFileObject.isReadable();
        } catch (FileSystemException fse) {
            // FileSystemException is not a Runtime Exception :(
            throw new RuntimeException(fse);
        }
    }

    // Override ALL public methods to throw Exceptions; 
    // implement or mock only the methods that you need.
}

至于为什么File(File, String)构造函数不能使用该设置:该构造函数不希望实现File破坏类的契约——我们在调用super("/tmp/xxxx"). (而且我们不能避免打破类的契约,因为我们想要使用的虚拟文件没有一个普通的File等价物)

所以,你来了——这需要做一些不平凡的工作,而且图书馆很可能无法按预期工作。

于 2010-12-13T12:21:54.290 回答