Mocking Files в Java - Mock Contents - Mockito
Я новичок в насмешках, и я пытаюсь высмеять фактическое содержимое (по существу, создаю виртуальный файл только в памяти), чтобы никакие данные не записывались на диск в любой момент.
Я пробовал решения, такие как издевательство над файлом и издевательство над многими из свойств, которые я могу определить как можно больше, а затем писать в него с помощью файла-сценариста/буферизатора, но это не работает, поскольку им нужны канонические пути. Кто-нибудь нашел решение, отличное от этого или подобного, но что я приближаюсь к этому неправильно?
Я делал это так:
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);
}
}
Любые идеи или рекомендации будут очень полезны.
Где-то после этого я в основном пытаюсь читать из файла с помощью другого класса. Я попытался бы издеваться над каким-то входным потоком, но другой класс не принимает входной поток, так как это класс обработки io для проекта.
Ответы
Ответ 1
Вы, кажется, следуете за противоречивыми целями. С одной стороны, вы пытаетесь избежать записи данных на диск, что не является плохой целью в тестах. С другой стороны, вы пытаетесь проверить свой класс обработки ввода-вывода, что означает, что вы будете работать с системными утилитами, которые предполагают, что ваш File
будет работать с собственными вызовами. Таким образом, здесь мое руководство:
- Не пытайтесь издеваться над
File
. Только не надо. От него зависит слишком много родных вещей.
- Если вы можете, разделите код обработки ввода-вывода на половину, который откроет
File
и превратит его в Reader
, а половину, которая анализирует HTML из Reader
.
- В этот момент вам вообще не нужен макет - просто создайте
StringReader
для имитации источника данных.
- В то время как эта процедура отлично справляется с вашими модульными тестами, вы также можете написать тест интеграции, который использует временный файл и убедиться, что он правильно читает. (Спасибо 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));
}
}
В этот момент логика в method
настолько проста, что не стоит тестировать,
и логика в methodForTest
настолько проста в доступе, что вы можете протестировать ее в значительной степени.
Ответ 2
Один способ издеваться над вызовами ввода/вывода (с 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);
}
}
Бизнес-логика находится в ImageManager
:
FileHelper fileHelperMock = Mockito.mock(new FileHelper());
ImageManager imageManager = new ImageManagerImpl(fileHelperMock);
Тест проверяет вызов createDirectory()
на ваш макет:
imageManager.save("directory");
Mockito.verify(fileHelperMock).createDirectory("directory");
Я бы использовал этот метод во время тестовой разработки, где я не хочу загрязнять тестирование с помощью реального управления файлами (например, удалять созданные каталоги/файлы в блоке finally в каждом модульном тесте).
Тогда у меня были бы приемочные тесты, охватывающие каждый прецедент с реальной обработкой файлов.