This article will describe how to mock and test the file system in Dart/Flutter for tests and in particular unit tests.
This article assumes that the reader is familiar with Flutter development and the Dart language.
The article is divided into:
1. Introduction
We want to create a function createHelloWorldFile
that creates a file hello_world.dart
at a location generated/hello_world.dart
.
We also want to unit test this function to ensure that the file is created at the specific location when the function runs.
We'll look at two different ways of doing this:
2. Using the dart:io library
Writing the Function
This implementation is to make use of the dart:io library and its File object to create the file.
We first create the File
object with the desired location passed as a parameter and call .createSync
to create the file.
recursive:true
is needed to ensure that any non-existing path is created first before creating the file.
Here is the code for the function:
import 'dart:io';
void createHelloWorldFile() {
final file = File('generated/hello_world.dart');
file.createSync(recursive: true);
}
Calling the Function
We can call the function like this:
import 'dart:io';
void main(List<String> arguments) {
createHelloWorldFile();
}
Testing the Function
The first step is to add the test library to dev_dependencies
in the pubspec.yaml
file like this:
dev_dependencies:
test:
The next step is to write the test for the function like this:
import 'dart:io';
import 'package:test/test.dart';
void main() {
test('createHelloWorldFile creates a file at generated/hello_world.dart', () {
createHelloWorldFile();
final file = File('generated/hello_world.dart');
expect(file.existsSync(), true);
});
}
The test code above uses the .existsSync property on the created File
object to confirm if it does exist.
The test passes when it is run.
The Problem with the Test
Running this test passes but the problem with this test is that it actually creates the generated/hello_world.dart
file.
This generated file is not needed for the actual test so it would be better to use a mock file system so we can write/run as many tests as we want without worrying about side effects.
3. Using the file library
Writing the Function
To solve the problem with the extra file generated, we will use the file library which gives the ability to create custom file systems and in-memory file systems for easy unit testing of code that works with file systems.
The first step is to add the file
library to dependecies
in our pubspec.yaml
file like this:
dependencies:
file:
The next step is to update our createHelloWorldFile
function to this:
import 'package:test/test.dart';
import 'package:file/file.dart';
void createHelloWorldFile(FileSystem fileSystem) {
final file = fileSystem.file('generated/hello_world.dart');
file.createSync(recursive: true);
}
In the code above, we inject the fileSystem into the function createHelloWorldFile
and use that to create the file.
Calling the Function
To call the function, we pass a LocalFileSystem object into the createHelloWorldFile
function.
LocalFileSystem class
A wrapper implementation around dart:io's implementation.
Since this implementation of the FileSystem interface delegates to dart:io, [it] is not suitable for use in the browser.
import 'package:file/local.dart';
void main(List<String> arguments) {
final localFileSystem = LocalFileSystem();
createHelloWorldFile(localFileSystem);
}
Testing the Function
In order to test the function, we can pass a MemoryFileSystem object into the createHelloWorldFile
.
MemoryFileSystem class
An implementation of FileSystem that exists entirely in memory with an internal representation loosely based on the Filesystem Hierarchy Standard.
MemoryFileSystem is suitable for mocking and tests, as well as for caching or staging before writing or reading to a live system.
import 'package:file/memory.dart';
import 'package:test/test.dart';
void main() {
test('createHelloWorldFile creates a file at generated/hello_world.dart', () {
final memoryFileSystem = MemoryFileSystem();
createHelloWorldFile(memoryFileSystem);
final file = memoryFileSystem.file('generated/hello_world.dart');
expect(file.existsSync(), true);
});
}
Running this test passes and it does not create any extra files.
4. Conclusion
The file library provides an easy way to test operations involving files with the use of the MemoryFileSystem
which is an in-memory file system that does not write to the actual file system.
The complete project can be found on Github.