Ваш вопрос раскрывает одну из самых сложных частей тестирования для разработчиков, которые только начинают это делать:
"Что, черт возьми, я тестирую?"
Ваш пример не очень интересен, потому что он просто склеивает некоторые вызовы API вместе, поэтому, если бы вы написали для него модульный тест, вы бы просто утверждали, что методы были вызваны. Подобные тесты тесно связывают детали вашей реализации с тестом. Это плохо, потому что теперь вам нужно менять тест каждый раз, когда вы меняете детали реализации вашего метода, потому что изменение деталей реализации нарушает ваш тест (ы)!
Плохие тесты на самом деле хуже, чем их полное отсутствие.
В вашем примере:
void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
string path = zipper.Unzip(theZipFile);
IFakeFile file = fileSystem.Open(path);
runner.Run(file);
}
Хотя вы можете передавать имитации, в методе тестирования нет логики. Если бы вы попытались выполнить модульный тест для этого, это могло бы выглядеть примерно так:
void testDoIt()
{
when(zipper.Unzip(any(File.class)).thenReturn("some path");
when(fileSystem.Open("some path")).thenReturn(mock(IFakeFile.class));
someObject.DoIt(zipper, fileSystem, runner);
verify(zipper).Unzip(any(File.class));
verify(fileSystem).Open("some path"));
verify(runner).Run(file);
}
Поздравляем, вы в основном скопировали детали реализации вашего DoIt()
метода в тест. Счастливого сохранения.
Когда вы пишете тесты, вы хотите проверить ЧТО, а не КАК .
Подробнее см. Тестирование черного ящика .
Что это имя вашего метода (или , по крайней мере , должно быть). Как все маленькие детали реализации , которые живут внутри вашего метода. Хорошие тесты позволяют вам заменить КАК, не нарушая ЧТО .
Подумайте об этом так, спросите себя:
«Если я изменю детали реализации этого метода (без изменения публичного контракта), нарушат ли мои тесты?»
Если да, то вы проверяете КАК, а не ЧТО .
Чтобы ответить на ваш конкретный вопрос о тестировании кода с зависимостями файловой системы, допустим, у вас было что-то более интересное, что происходит с файлом, и вы хотите сохранить закодированное в Base64 содержимое a byte[]
в файл. Вы можете использовать потоки для этого, чтобы проверить, что ваш код работает правильно, без необходимости проверять, как он это делает. Один из примеров может быть примерно таким (на Java):
interface StreamFactory {
OutputStream outStream();
InputStream inStream();
}
class Base64FileWriter {
public void write(byte[] contents, StreamFactory streamFactory) {
OutputStream outputStream = streamFactory.outStream();
outputStream.write(Base64.encodeBase64(contents));
}
}
@Test
public void save_shouldBase64EncodeContents() {
OutputStream outputStream = new ByteArrayOutputStream();
StreamFactory streamFactory = mock(StreamFactory.class);
when(streamFactory.outStream()).thenReturn(outputStream);
Base64FileWriter fileWriter = new Base64FileWriter();
fileWriter.write("Man".getBytes(), streamFactory);
assertThat(outputStream.toString()).isEqualTo("TWFu");
}
Тест использует , ByteArrayOutputStream
но в применении ( с помощью инъекции зависимостей) реальный StreamFactory (возможно , называется FileStreamFactory) вернется FileOutputStream
из outputStream()
и написал бы к File
.
Что было интересно в этом write
методе, так это то, что он записывал содержимое в кодировке Base64, поэтому мы и тестировали именно это. Для вашего DoIt()
метода это было бы более уместно протестировать с помощью интеграционного теста .