创建一个无法通过file.delete()删除的文件

时间:2018-06-12 16:56:58

标签: java file junit4

注意:我知道有几个类似于这个的问题,但是,我找不到任何解释如何解决我想要解决的情况。我将用一个具体的例子来问这个问题,我需要一个解决方案。

考虑代码:

private final void writeToFile(final File parent, final String filename, final Charset charset, final String content) throws IOException {
    final File file = new File(parent, filename);

    if (file.exists()) {
        LOG.warn("File {} already exists, file will be replaced.", file.getCanonicalPath());
        if (!file.delete()) {
            logAndThrow(String.format("Cannot delete file '%s'.", file.getCanonicalPath()), null);
        }
    }

    try (final FileOutputStream fos = new FileOutputStream(file);
            OutputStreamWriter writer = new OutputStreamWriter(fos, charset)) {
        writer.write(content);
    }
}

我正在尝试编写单元测试,以在代码无法删除文件时激发抛出的IOException。我尝试过的单元测试如下:

@Test public void testFileNotDeletable() throws IOException {
  final File file = new File(folder.getRoot(), formattedFile.getMetaData().getFormattedCaptureFileName());
  file.createNewFile();
  try {
    file.setReadOnly();

    exception.expect(IOException.class);
    exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));

    writer.write(formattedFile);
  } finally {
    file.setWritable(true);
  }
}

我也试过锁定文件:

@Test public void testFileNotDeletable() throws IOException {
    final File file = new File(folder.getRoot(), formattedFile.getMetaData().getFormattedCaptureFileName());
    file.createNewFile();
    try (FileInputStream fis = new FileInputStream(file)) {
        final FileLock lock = fis.getChannel().tryLock(0L, Long.MAX_VALUE, true);
        try {
            exception.expect(IOException.class);
            exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));

            writer.write(formattedFile);
        } finally {
            lock.release();
        }
    }
}

无论我尝试什么,file.delete()都会成功删除文件,测试失败,因为没有抛出预期的IOException。

非常感谢任何帮助。

注意:为了澄清,添加了一些额外的代码,表明File对象在环境中是完全独立的。传递给write方法的formattedFile不是File的文件或子类,它是我们的内部类之一。 JUnit测试中的File使用TemporaryFolder作为root,formattedFile有一个MetaData项,用于确定文件名。在我的JUnit测试中,我试图在我的实际代码将尝试写入文件的位置创建一个无法删除的空文件。我需要file.delete()来返回false,以便我可以测试异常被抛出。因此,我无法模拟File对象。

5 个答案:

答案 0 :(得分:1)

您的问题有两个解决方案,我推荐第一个。

  • 解决方案1 ​​ 您没有在这里测试java文件I / O操作/类,您正在测试代码的功能行为以响应文件操作。所以,理想情况下,在你的JUnit中,你应该嘲笑File对象&它各自的电话,只专注于测试您的代码。

  • 解决方案2 如果您仍希望测试与java文件IO的完全集成,请在尝试删除之前以写入模式打开文件,它将处理您的测试用例。

注意:在CENTOS,WINDOWS,UBUNTU,MAC OS-X中测试的代码

主题类:

    public class FileSolution {
        public void fileHandler(File file) throws IOException, Exception {
            if (file.exists()) {
                LOG.warn("File {} already exists, file will be replaced.", 
                        file.getCanonicalPath());
                if (!file.delete()) {
                    logAndThrow(String.format("Cannot delete file '%s'.", 
                            file.getCanonicalPath()),
                            new IOException(String.format("Cannot delete file '%s'.", 
                                    file.getCanonicalPath())));
                }
            }
        }
    }

主题Uner测试:

import static org.mockito.BDDMockito.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class FileSolutionTest {

    @Rule
    public final ExpectedException exception = ExpectedException.none();

    /**
     * Solution 1
     * 
     * @throws Exception
     */
    @Test
    public void testFileNotDeletableWithMock() throws Exception {
        final File file = mock(File.class);
        file.createNewFile();
        // mock file & IO operations
        given(file.exists()).willReturn(true);
        given(file.delete()).willReturn(false);
        given(file.getCanonicalPath()).willReturn("test.txt");

        exception.expect(IOException.class);
        exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));

        new FileSolution().fileHandler(file);
    }

    /**
     * Solution 2
     * 
     * @throws Exception
     */
    @Test
    public void testFileNotDeletable() throws Exception {
        File file = null;
        FileWriter fileWriter = null;
        try{
            file = new File("test.txt");
            file.createNewFile();
            file.deleteOnExit();
            exception.expect(IOException.class);
            exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));
            // open file with another process for writing
            fileWriter = new FileWriter(file, true);
            new FileSolution().fileHandler(file);
        } finally{
            if(fileWriter != null){
                fileWriter.flush();
                fileWriter.close();
            }
        }
    }
}

答案 1 :(得分:0)

为了防止文件被删除,您必须拒绝Windows中的安全权限。从UI我们需要做类似

的事情
  1. 右键单击PC中的文件或文档=>选择属性;
  2. 在安全性中,选项卡编辑以更改权限=>选择添加并输入所有人;
  3. 按确定,然后选择要将完全控制权限更改为拒绝的组;
  4. 按是确认。
  5. 我知道用Java更改文件权限的唯一方法是:

    file.setExecutable(true|false);
    file.setReadable(true|false);
    file.setWritable(true|false);
    

    File file = new File("test.txt");
    
    if(file.exists())
    {
        //Setting file permissions for owner, group and others using PosixFilePermission
    
        HashSet<PosixFilePermission> set = new HashSet<PosixFilePermission>();
    
        //Adding owner's file permissions
    
        set.add(PosixFilePermission.OWNER_EXECUTE);
        set.add(PosixFilePermission.OWNER_READ);
        set.add(PosixFilePermission.OWNER_WRITE);
    
        //Adding group's file permissions
    
        set.add(PosixFilePermission.GROUP_EXECUTE);
        set.add(PosixFilePermission.GROUP_READ);
        set.add(PosixFilePermission.GROUP_WRITE);
    
        //Adding other's file permissions
    
        set.add(PosixFilePermission.OTHERS_EXECUTE);
        set.add(PosixFilePermission.OTHERS_READ);
        set.add(PosixFilePermission.OTHERS_WRITE);
    
        Files.setPosixFilePermissions(Paths.get("test.txt"), set);
    }
    else
    {
        System.out.println("Sorry...File doesn't exist.");
    }
    

    因此,防止文件被删除,我认为这与文件写入权限有关。在尝试删除文件之前,请尝试禁用可写和可执行权限。

    如果这不起作用,那么我不相信它可以用Java语言完成,因为这些是目前唯一可用于更改文件权限的方法。我错了,但我一直找不到其他的东西。

    更新

    对于Linux,请尝试以下操作:

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    
    public class ExecuteShellComand {
    
        public static void main(String[] args) {
    
            ExecuteShellComand obj = new ExecuteShellComand();
    
            String command = "sudo chattr +i /backups/passwd";
            // OR try
    
            //String command = "sudo chattr +i -V /backups/passwd";
    
            String output = obj.executeCommand(command);
    
            System.out.println(output);
    
        }
    
        private String executeCommand(String command) {
    
            StringBuffer output = new StringBuffer();
    
            Process p;
            try {
                p = Runtime.getRuntime().exec(command);
                p.waitFor();
                BufferedReader reader = 
                                new BufferedReader(new InputStreamReader(p.getInputStream()));
    
                            String line = "";           
                while ((line = reader.readLine())!= null) {
                    output.append(line + "\n");
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return output.toString();
    
        }
    }
    

    以上使/ backups / passwd文件不可变(或不可删除)。这意味着无法以任何方式修改文件:无法删除或重命名。您甚至无法创建指向它的链接,也不能将任何数据写入该文件。 这是我唯一能想到的。

    希望这会有所帮助。

答案 2 :(得分:0)

我完全同意Turing85关于使用mockito的信息。

让我们假设您有一个原始类,其方法类似于您要测试的方法:

public class FileDel {

    public void logOnIOException(File file) throws IOException {
        if (file.exists()) {
            LOG.warn("File {} already exists, file will be replaced.", file.getCanonicalPath());
            if (!file.delete()) {
                logAndThrow(String.format("Cannot delete file '%s'.", file.getCanonicalPath()), null);
            }
        }
    }

    public void logAndThrow(String msg, String s) {
        //Do nothing
    }

    private static class LOG {
        public static void warn(String msg, String path) {
        }
    }
}

然后你可以用这种方式触发内部异常:

@RunWith(MockitoJUnitRunner.class)
public class FileDelTest {
    @Test(expected = IOException.class)
    public void testFileNotDeletable() throws IOException {
        File file = mock(File.class);
        when(file.exists()).thenReturn(true);
        when(file.delete()).thenAnswer(new Answer<Boolean>() {
            @Override
            public Boolean answer(InvocationOnMock iom) throws Throwable {
                throw new IOException();
            }
        });

        FileDel f = new FileDel();

        try {
            f.methodToTest(file);
        } finally {
        }
    }
}

答案 3 :(得分:0)

如何为此文件打开InputStream并且不要关闭它。直到文件的描述符不会被关闭,文件才会被删除。

答案 4 :(得分:0)

在Linux中,您可以使用chattr命令设置一个“不可变”的文件,即使是root也无法删除。其他人说“设置文件权限”这是正确的,但没有给出具体细节。

干杯 d