Java中的模拟文件-Mock Contents-Mockito
我对模拟非常陌生,我一直在尝试模拟实际内容(基本上只在内存中创建一个虚拟文件),以便在任何时候都不会将数据写入磁盘 我尝试过一些解决方案,比如模拟文件,模拟尽可能多的属性,然后用filewriter/bufferedwriter将其写入,但这些方法效果不好,因为它们需要规范路径。有人找到了除此之外的解决方案或类似的解决方案,但我的做法是错误的吗 我一直是这样做的:Java中的模拟文件-Mock Contents-Mockito,java,file,mockito,Java,File,Mockito,我对模拟非常陌生,我一直在尝试模拟实际内容(基本上只在内存中创建一个虚拟文件),以便在任何时候都不会将数据写入磁盘 我尝试过一些解决方案,比如模拟文件,模拟尽可能多的属性,然后用filewriter/bufferedwriter将其写入,但这些方法效果不好,因为它们需要规范路径。有人找到了除此之外的解决方案或类似的解决方案,但我的做法是错误的吗 我一直是这样做的: private void mocking(){ File badHTML = mock(File.class); /
private void mocking(){
File badHTML = mock(File.class);
//setting the properties of badHTML
when(badHTML.canExecute()).thenReturn(Boolean.FALSE);
when(badHTML.canRead()).thenReturn(Boolean.TRUE);
when(badHTML.canWrite()).thenReturn(Boolean.TRUE);
when(badHTML.compareTo(badHTML)).thenReturn(Integer.SIZE);
when(badHTML.delete()).thenReturn(Boolean.FALSE);
when(badHTML.getFreeSpace()).thenReturn(0l);
when(badHTML.getName()).thenReturn("bad.html");
when(badHTML.getParent()).thenReturn(null);
when(badHTML.getPath()).thenReturn("bad.html");
when(badHTML.getParentFile()).thenReturn(null);
when(badHTML.getTotalSpace()).thenReturn(0l);
when(badHTML.isAbsolute()).thenReturn(Boolean.FALSE);
when(badHTML.isDirectory()).thenReturn(Boolean.FALSE);
when(badHTML.isFile()).thenReturn(Boolean.TRUE);
when(badHTML.isHidden()).thenReturn(Boolean.FALSE);
when(badHTML.lastModified()).thenReturn(System.currentTimeMillis());
when(badHTML.mkdir()).thenReturn(Boolean.FALSE);
when(badHTML.mkdirs()).thenReturn(Boolean.FALSE);
when(badHTML.setReadOnly()).thenReturn(Boolean.FALSE);
when(badHTML.setExecutable(true)).thenReturn(Boolean.FALSE);
when(badHTML.setExecutable(false)).thenReturn(Boolean.TRUE);
when(badHTML.setReadOnly()).thenReturn(Boolean.FALSE);
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(badHTML));
/*
badHTMLText is a string with the contents i want to put into the file,
can be just about whatever you want
*/
bw.append(badHTMLText);
bw.close();
} catch (IOException ex) {
System.err.println(ex);
}
}
任何想法或指导都会非常有用。
在这之后的某个地方,我基本上尝试使用另一个类读取文件。我会尝试模拟某种输入流,但另一个类不接受inputstream,因为它是项目的io处理类。您似乎在追求相互矛盾的目标。一方面,您试图避免将数据写入磁盘,这在测试中是一个不错的目标。另一方面,您正在尝试测试I/O处理类,这意味着您将使用假定您的
文件
将处理本机调用的系统实用程序。因此,以下是我的指导:
- 不要试图模仿
。别这样。太多的本土事物依赖于它文件
- 如果可以,将I/O处理代码拆分为打开
文件的一半,并将其转换为
,以及从读取器
读取器中解析HTML的一半
- 在这一点上,您根本不需要模拟——只需要构造一个模拟数据源的模型
- 虽然这样可以很好地处理单元测试,但您可能还希望编写一个使用的集成测试,并确保它的读取正确。(感谢Brice添加此提示!)
class YourClass {
public int method(File file) {
// do everything here, which is why it requires a mock
}
}
class YourRefactoredClass {
public int method(File file) {
return methodForTest(file.getName(), file.isFile(),
file.isAbsolute(), new FileReader(file));
}
/** For testing only. */
int methodForTest(
String name, boolean isFile, boolean isAbsolute, Reader fileContents) {
// actually do the calculation here
}
}
class YourTest {
@Test public int methodShouldParseBadHtml() {
YourRefactoredClass yrc = new YourRefactoredClass();
assertEquals(42, yrc.methodForTest(
"bad.html", true, false, new StringReader(badHTMLText));
}
}
此时,方法
中的逻辑非常简单,不值得测试,
methodForTest
中的逻辑非常容易访问,因此您可以对其进行大量测试。模拟I/O调用的一种方法(对于Java 7,将是NIO最终类Java.NIO.file.Files
)是将所需的调用封装在您自己的类中并模拟它:
public class FileHelper {
public Path createDirectory(String directoryName) throws IOException {
return Files.createDirectory(Paths.get(directoryName));
}
public boolean exists(String name) throws IOException {
return Files.exists(Paths.get(name), LinkOption.NOFOLLOW_LINKS);
}
}
业务逻辑位于图像管理器中
:
FileHelper fileHelperMock = Mockito.mock(new FileHelper());
ImageManager imageManager = new ImageManagerImpl(fileHelperMock);
该测试将验证对模拟上的createDirectory()
方法的调用:
imageManager.save("directory");
Mockito.verify(fileHelperMock).createDirectory("directory");
在测试驱动的开发过程中,我会使用这种方法,我不想用真正的文件管理(例如,在每个单元测试的finally块中删除创建的目录/文件)来污染测试
然后,我将进行验收测试,测试将覆盖每个具有真实文件处理的用例。测试的目标是什么?为什么不模拟BufferedWriter呢?BufferedWriter将文件写入该文件中,以便其他类可以处理模拟的文件。另一个测试用于测试html文件的内容并对其进行分级。请尝试spy(新文件(…)而不是模拟文件)。然后,您可以在when()中只更改您真正想要更改的行为,而不必处理整个API。不要模拟或监视文件!这不是正确的方法。这是一个测试反模式。一般来说,不要模仿你不拥有的类型!。相反,重点是在访问文件的代码和处理内容的代码之间拆分功能。杰夫的答案是正确的!我想说的是,您完全可以(而且在某些情况下应该)模拟您不拥有的代码,但只有接口!您的代码无论如何都应该针对接口,而不是具体的实现,如果可能的话。但是java.io.File不满足接口要求。所以不适合模拟。+1用于不模拟文件并将逻辑拆分为具有不同关注点的不同对象。同样,从更一般的角度来说,模仿你不拥有的类型根本不是一个好主意。所以我要重新表述Jeff所说的,但要编写一个集成测试,该测试将断言将打开和/或写入文件的代码,并提供您自己的类型或
读取器
/编写器
,并为将处理内容的代码编写一个单元测试,当然,您不必嘲笑阅读器
/编写器
,而是在现有实现上使用或断言内容,如StringReader
/StringWriter
。是的,如果我可以一直编辑它,我会这么做。从技术上讲,我可以编辑它,但不能,因为它会破坏其他人当前的可用性。Matt--就像上面的方法
一样,您通常可以插入自己的测试访问,而无需实际更改外部API。我知道这可能不是项目中你的一部分,但如果你的工作是测试其他人难以测试的类,那么通常值得尝试移动一些东西,使其更干净、更容易。祝你好运!:)谢谢我决定完全忽略它,甚至不尝试模拟这些,只需要使用真实的文件,因为尝试模拟任何东西或替换任何妨碍其余工作的东西。使用虚拟文件系统(如JIMFS)怎么样?当我测试将文件表单a复制到B的服务,并让它复制到虚拟文件系统中时,这仍然是一个单元测试吗?这是个好主意吗?