Sistem File Mocking dengan Jimfs

1. Ikhtisar

Biasanya, saat menguji komponen yang membuat banyak penggunaan operasi I / O, pengujian kami dapat mengalami beberapa masalah seperti kinerja yang buruk, ketergantungan platform, dan status yang tidak terduga.

Dalam tutorial ini, kita akan melihat bagaimana kita dapat mengatasi masalah ini menggunakan sistem file dalam memori Jimfs.

2. Pengantar Jimfs

Jimfs adalah sistem file dalam memori yang mengimplementasikan Java NIO API dan mendukung hampir semua fiturnya. Ini sangat berguna, karena ini berarti kita dapat meniru sistem file dalam memori virtual dan berinteraksi dengannya menggunakan lapisan java.nio yang ada .

Seperti yang akan kita lihat, mungkin bermanfaat untuk menggunakan sistem file tiruan daripada yang asli untuk:

  • Hindari bergantung pada sistem file yang sedang menjalankan pengujian
  • Pastikan sistem file dirakit dengan keadaan yang diharapkan pada setiap pengujian yang dijalankan
  • Membantu mempercepat pengujian kami

Karena sistem file sangat bervariasi, penggunaan Jimfs juga memudahkan pengujian dengan sistem file dari sistem operasi yang berbeda.

3. Ketergantungan Maven

Pertama-tama, mari tambahkan dependensi proyek yang kita perlukan untuk contoh kita:

 com.google.jimfs jimfs 1.1 

Ketergantungan jimfs berisi semua yang kita butuhkan untuk menggunakan sistem file tiruan kita. Selain itu, kami akan menulis pengujian menggunakan JUnit5.

4. Tempat Penyimpanan File Sederhana

Kita akan mulai dengan mendefinisikan kelas FileRepository sederhana yang mengimplementasikan beberapa operasi CRUD standar:

public class FileRepository { void create(Path path, String fileName) { Path filePath = path.resolve(fileName); try { Files.createFile(filePath); } catch (IOException ex) { throw new UncheckedIOException(ex); } } String read(Path path) { try { return new String(Files.readAllBytes(path)); } catch (IOException ex) { throw new UncheckedIOException(ex); } } String update(Path path, String newContent) { try { Files.write(path, newContent.getBytes()); return newContent; } catch (IOException ex) { throw new UncheckedIOException(ex); } } void delete(Path path) { try { Files.deleteIfExists(path); } catch (IOException ex) { throw new UncheckedIOException(ex); } } }

Seperti yang bisa kita lihat, setiap metode menggunakan kelas java.nio standar .

4.1. Membuat File

Di bagian ini, kita akan menulis tes yang menguji metode create dari repositori kita:

@Test @DisplayName("Should create a file on a file system") void givenUnixSystem_whenCreatingFile_thenCreatedInPath() { FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix()); String fileName = "newFile.txt"; Path pathToStore = fileSystem.getPath(""); fileRepository.create(pathToStore, fileName); assertTrue(Files.exists(pathToStore.resolve(fileName))); }

Dalam contoh ini, kami telah menggunakan metode statis Jimfs.newFileSystem () untuk membuat sistem file dalam memori baru. Kami meneruskan objek konfigurasi Configuration.unix () , yang membuat konfigurasi tetap untuk sistem file Unix . Ini termasuk informasi khusus OS yang penting seperti pemisah jalur dan informasi tentang tautan simbolik.

Sekarang setelah kami membuat file, kami dapat memeriksa apakah file tersebut berhasil dibuat di sistem berbasis Unix.

4.2. Membaca File

Selanjutnya, kami akan menguji metode yang membaca konten file:

@Test @DisplayName("Should read the content of the file") void givenOSXSystem_whenReadingFile_thenContentIsReturned() throws Exception { FileSystem fileSystem = Jimfs.newFileSystem(Configuration.osX()); Path resourceFilePath = fileSystem.getPath(RESOURCE_FILE_NAME); Files.copy(getResourceFilePath(), resourceFilePath); String content = fileRepository.read(resourceFilePath); assertEquals(FILE_CONTENT, content); }

Kali ini, kami telah memeriksa apakah mungkin membaca konten file pada sistem macOS (sebelumnya OSX) hanya dengan menggunakan jenis konfigurasi yang berbeda - Jimfs.newFileSystem (Configuration.osX ()) .

4.3. Memperbarui File

Kami juga dapat menggunakan Jimfs untuk menguji metode yang memperbarui konten file:

@Test @DisplayName("Should update the content of the file") void givenWindowsSystem_whenUpdatingFile_thenContentHasChanged() throws Exception { FileSystem fileSystem = Jimfs.newFileSystem(Configuration.windows()); Path resourceFilePath = fileSystem.getPath(RESOURCE_FILE_NAME); Files.copy(getResourceFilePath(), resourceFilePath); String newContent = "I'm updating you."; String content = fileRepository.update(resourceFilePath, newContent); assertEquals(newContent, content); assertEquals(newContent, fileRepository.read(resourceFilePath)); }

Demikian juga, kali ini kami telah memeriksa bagaimana metode berperilaku pada sistem berbasis Windows dengan menggunakan Jimfs.newFileSystem (Configuration.windows ()) .

4.4. Menghapus File

Untuk menyimpulkan pengujian operasi CRUD kita, mari kita uji metode yang menghapus file tersebut:

@Test @DisplayName("Should delete file") void givenCurrentSystem_whenDeletingFile_thenFileHasBeenDeleted() throws Exception { FileSystem fileSystem = Jimfs.newFileSystem(); Path resourceFilePath = fileSystem.getPath(RESOURCE_FILE_NAME); Files.copy(getResourceFilePath(), resourceFilePath); fileRepository.delete(resourceFilePath); assertFalse(Files.exists(resourceFilePath)); }

Tidak seperti contoh sebelumnya, kami telah menggunakan Jimfs.newFileSystem () tanpa menentukan konfigurasi sistem file. Dalam hal ini, Jimfs akan membuat sistem file dalam memori baru dengan konfigurasi default yang sesuai dengan sistem operasi saat ini.

5. Memindahkan File

Di bagian ini, kita akan belajar cara menguji metode yang memindahkan file dari satu direktori ke direktori lainnya.

Pertama, mari kita implementasikan metode pindah menggunakan kelas java.nio.file.File standar :

void move(Path origin, Path destination) { try { Files.createDirectories(destination); Files.move(origin, destination, StandardCopyOption.REPLACE_EXISTING); } catch (IOException ex) { throw new UncheckedIOException(ex); } }

Kami akan menggunakan pengujian berparameter untuk memastikan bahwa metode ini berfungsi pada beberapa sistem file yang berbeda:

private static Stream provideFileSystem() { return Stream.of( Arguments.of(Jimfs.newFileSystem(Configuration.unix())), Arguments.of(Jimfs.newFileSystem(Configuration.windows())), Arguments.of(Jimfs.newFileSystem(Configuration.osX()))); } @ParameterizedTest @DisplayName("Should move file to new destination") @MethodSource("provideFileSystem") void givenEachSystem_whenMovingFile_thenMovedToNewPath(FileSystem fileSystem) throws Exception { Path origin = fileSystem.getPath(RESOURCE_FILE_NAME); Files.copy(getResourceFilePath(), origin); Path destination = fileSystem.getPath("newDirectory", RESOURCE_FILE_NAME); fileManipulation.move(origin, destination); assertFalse(Files.exists(origin)); assertTrue(Files.exists(destination)); }

Seperti yang dapat kita lihat, kita juga dapat menggunakan Jimfs untuk menguji apakah kita dapat memindahkan file pada berbagai sistem file yang berbeda dari satu unit test.

6. Tes Ketergantungan Sistem Operasi

Untuk mendemonstrasikan manfaat lain menggunakan Jimfs, mari buat kelas FilePathReader . Kelas bertanggung jawab untuk mengembalikan jalur sistem sebenarnya, yang tentu saja bergantung pada OS:

class FilePathReader { String getSystemPath(Path path) { try { return path .toRealPath() .toString(); } catch (IOException ex) { throw new UncheckedIOException(ex); } } }

Sekarang, mari tambahkan tes untuk kelas ini:

class FilePathReaderUnitTest { private static String DIRECTORY_NAME = "baeldung"; private FilePathReader filePathReader = new FilePathReader(); @Test @DisplayName("Should get path on windows") void givenWindowsSystem_shouldGetPath_thenReturnWindowsPath() throws Exception { FileSystem fileSystem = Jimfs.newFileSystem(Configuration.windows()); Path path = getPathToFile(fileSystem); String stringPath = filePathReader.getSystemPath(path); assertEquals("C:\\work\\" + DIRECTORY_NAME, stringPath); } @Test @DisplayName("Should get path on unix") void givenUnixSystem_shouldGetPath_thenReturnUnixPath() throws Exception { FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix()); Path path = getPathToFile(fileSystem); String stringPath = filePathReader.getSystemPath(path); assertEquals("/work/" + DIRECTORY_NAME, stringPath); } private Path getPathToFile(FileSystem fileSystem) throws Exception { Path path = fileSystem.getPath(DIRECTORY_NAME); Files.createDirectory(path); return path; } }

Seperti yang bisa kita lihat, keluaran untuk Windows berbeda dari yang ada di Unix, seperti yang kita harapkan. Selain itu, kami tidak perlu menjalankan pengujian ini menggunakan dua sistem file yang berbeda - Jimfs mengejeknya untuk kami secara otomatis .

It's worth mentioning that Jimfs doesn't support the toFile() method that returns a java.io.File. It's the only method from the Path class that isn't supported. Therefore, it might be better to operate on an InputStream rather than a File.

7. Conclusion

In this article, we've learned how to use use the in-memory file system Jimfs to mock file system interactions from our unit tests.

First, we started by defining a simple file repository with several CRUD operations. Then we saw examples of how to test each of the methods using a different file system type. Finally, we saw an example of how we can use Jimfs to test OS-dependent file system handling.

Seperti biasa, kode untuk contoh ini tersedia di Github.