... in particular when it comes to the file I/O. I actually have a function like
pub fn update(job: Job, file: &str) -> Result<(), failure::Error> {
let db_raw = fs::read_to_string(file)
.context(format!("Could not read the job database, {}", file))?;
let mut db: JobDB = toml::from_str(&db_raw)?;
db.push(job);
let db_raw = toml::to_string_pretty(&db).expect("New DB malformed");
fs::write(file, db_raw).context("Could not write the new DB")?;
Ok(())
}
One way could be create an auxiliary function that would accept a String
and return a String
but this won't catch any bugs in the I/O handling. One could have a file some temporary directory but this sounds like an easy way to lose platform-independence.
2 Likes
You can change the fn to be generic over Read + Write
(rather than hardcoding fs::read_to_string
/fs::write
) and then provide a test/mock Read/Write
impl that induces cases you'd like to test.
4 Likes
The tempfile crate provides platform-independent temporary directory APIs.
3 Likes
In my business logic I'll usually mock out all IO by pulling the IO operation out into its own trait (or reuse an existing trait like Read
or Write
), that way you get faster and more reliable tests. It also helps decouple the components in your application.
Testing error handling and various edge cases can then be done by passing in a specially crafted mock (e.g. a Write
implementation which always errors) and seeing what happens.
If you're testing IO code itself you pretty much need to do the operation (e.g. Rust std
implementing File::write()
). Typically you'd execute the IO in a temporary environment (e.g. tempfile
or a db you spin up just for that test) where you don't care about rogue tests breaking the world.
As an anecdote, one of the things we do at work is control actual mechanical machines and my initial reaction was to mock things out so the library is decoupled/insulated from the real world. Then one of the other engineers pointed out that the only way to make sure the machine moves when you hit a button is by actually hitting a button and seeing the machine move... That said, this is still done in a "safe" testing environment and not on customer machines.
Obviously you're just using that as an example, but if tempfile
behaved differently on Windows to Linux I'd say its a bug in the tempfile
crate. Whatever you're using to create a temporary environment/mock db/whatever should present a platform-independent interface to its users wherever possible.
2 Likes