【问题标题】:How to create an in-memory JarFile?如何创建内存中的 JarFile?
【发布时间】:2011-07-05 22:31:06
【问题描述】:

我正在尝试编写如下函数:

public Map<String, Document> getTestXml(JarFile jarFile) {
    Map<String, Document> result = Maps.newHashMap();

    Enumeration<JarEntry> jarEntries = jarFile.getEntries();
    while (jarEntries.hasMoreElements()) {
        JarEntry jarEntry = jarEntries.nextElement();

        String name = jarEntry.getName();
        if (name.endsWith(".class") && !name.contains("$")) {
            String testClassName = name.replace(".class", "").replace("/", ".");
            String testXmlFilename = "TEST-" + testClassName + ".xml";

            InputStream testXmlInputStream = testJarFile.getInputStream(
                    testJarFile.getJarEntry(testXmlFilename));

            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document testXmlDocument = documentBuilder.parse(testXmlInputStream);

            result.put(testClassName, testXmlDocument);
        }
    }

    return result;
}

而且我想编写一个实际上不会在文件系统上创建 JarFile 的单元测试。我试图寻找如何在内存中创建 File 对象,但没有找到类似的东西。有人有什么建议吗?

【问题讨论】:

    标签: java unit-testing jar in-memory


    【解决方案1】:

    使用 JarInputStream 代替 JarFile。为了测试,将 JarInputStream 连接到加载了内存中 jar 数据的 ByteArrayInputStream,并在正常操作中将其连接到来自文件的输入流。

    【讨论】:

      【解决方案2】:

      File() 对象都存在于某个文件系统名称空间中。这为您提供了两个基本选择:

      1)。如果您使用的是带有 tempfs 文件系统的操作系统,请在此处创建它。 2)。使用 File.createTempFile() 并设置 delete-on-exit 属性。

      创建子类的常用方法(“public MemoryFile extends File”...)不起作用,因为 File() 对象不包含执行实际 I/O 的方法,仅用于保存对象的名称并执行一些文件系统操作。

      【讨论】:

      • 关于上面的 JarInputStream 答案,该方法有效,但创建 Jar 需要使用 JarOutputStream。重新编写代码以便始终使用 JarInputStream 和 JarOutputStream 将是一个解决方案,如果传递一对对象不麻烦的话。使用 JarFile 几乎需要创建一个临时文件,这并不是那么糟糕的体验。
      【解决方案3】:

      您需要查看ByteArrayOutputStreamByteArrayInputStream。这些是 Java 中的 in memory 流对象。使用这些,什么都不会写入磁盘。

      【讨论】:

        【解决方案4】:

        您可以使用EasyMock 创建一个JarFile 类的模拟对象。对于模拟对象,您可以指定在测试中调用哪些方法以及返回值是什么,而无需在文件系统上实际创建 JAR 文件。

        然后使用您的模拟 JarFile 实例调用您的 getTestXml() 方法。

        它需要一些时间来适应它,但是你会发现它是值得的。

        更新 给定的源代码无法编译,所以这里是一个可编译的版本:

        public class JarFileUser {
          public Map<String, Document> getTestXml(JarFile jarFile) throws IOException, ParserConfigurationException, SAXException {
            Map<String, Document> result = new HashMap<String, Document>();
        
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
              JarEntry jarEntry = jarEntries.nextElement();
        
              String name = jarEntry.getName();
              if (name.endsWith(".class") && !name.contains("$")) {
                String testClassName = name.replace(".class", "").replace("/", ".");
                String testXmlFilename = "TEST-" + testClassName + ".xml";
        
                InputStream testXmlInputStream = jarFile.getInputStream(jarFile.getJarEntry(testXmlFilename));
        
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                Document testXmlDocument = documentBuilder.parse(testXmlInputStream);
        
                result.put(testClassName, testXmlDocument);
              }
            }
        
            return result;
          }
        }
        

        这是一个使用 EasyMock 的测试:

        public class JarFileUserTest {
        
          private JarFile mockJarFile;
          private Enumeration<JarEntry> mockJarEntries;
        
          private JarFileUser jarFileUser;
          private JarEntry first;
          private JarEntry second;
          private JarEntry firstXml;
        
          @Before
          public void setUp() throws Exception {
            jarFileUser = new JarFileUser();
        
            // Create a mock for the JarFile parameter
            mockJarFile = createMock(JarFile.class);
        
            // User Vector to provide an Enumeration of JarEntry-Instances 
            Vector<JarEntry> entries = new Vector<JarEntry>();
            first = createMock(JarEntry.class);
            second = createMock(JarEntry.class);
        
            entries.add(first);
            entries.add(second);
        
            expect(first.getName()).andReturn("mocktest.JarFileUser.class");
            expect(second.getName()).andReturn("mocktest.Ignore$Me.class");
        
            mockJarEntries = entries.elements();
        
            expect(mockJarFile.entries()).andReturn(mockJarEntries);
        
            // JarEntry for the XML file
            firstXml = createMock(JarEntry.class);
        
            expect(mockJarFile.getJarEntry("TEST-mocktest.JarFileUser.xml")).andReturn(firstXml);
        
            // XML contents
            ByteArrayInputStream is = new ByteArrayInputStream("<test>This is a test.</test>".getBytes("UTF-8"));
            expect(mockJarFile.getInputStream(firstXml)).andReturn(is);
        
            replay(mockJarFile);
            replay(first);
            replay(second);
            replay(firstXml);
          }
        
          @Test
          public void testGetTestXml() throws IOException, ParserConfigurationException, SAXException {
            Map<String, Document> map = jarFileUser.getTestXml(mockJarFile);
            verify(mockJarFile);
            verify(first);
            verify(second);
            verify(firstXml);
        
            assertEquals(1, map.size());
            Document doc = map.get("mocktest.JarFileUser");
            assertNotNull(doc);
            final Element root = (Element) doc.getDocumentElement();
            assertNotNull(root);
            assertEquals("test", root.getNodeName());
            assertEquals("This is a test.", root.getTextContent());
          }
        
        }
        

        关于其他库的说明 JarFile 是一个类而不是一个接口,因此根据EasyMock installation docs,您的类路径中应该有Objenesiscglib

        【讨论】:

        • 我目前正在使用 JMockit。当我尝试模拟 JarFile 时,我得到:线程“main”中的异常 java.lang.NoClassDefFoundError: org/junit/internal/runners/model/MultipleFailureException 你确定 EasyMock 能够处理 JarFile?
        猜你喜欢
        • 1970-01-01
        • 2018-06-25
        • 2012-02-28
        • 2018-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-06
        • 2011-09-22
        相关资源
        最近更新 更多