I have been taught to access databases through abstraction layers. I was wondering why it is not also standard practice to access the file system through an abstraction layer? It seems to me unit testing would become much simpler, since mocking the filesystem is often a pain and poorly supported by some languages.
In the vast majority of languages and frameworks, the stream is an abstract concept, and methods can be designed to accept generic streams (which can be substituted for in-memory data) instead of concrete file system objects.
I personally have not found much benefit to unit-testing file operations beyond that level, because there are so many things that can go wrong in practice that the unit tests prove almost nothing at all about the application's ability to perform its function.
It's actually a lot like database abstractions; typically you don't try to abstract something like the database connection or an ORM session/context/whatever, you just create a "repository" or "query" abstraction to encapsulate all the data access and mock that. So file I/O isn't that much different; you don't generally want to abstract the file system itself, just the operation being performed on the file system expressed in terms of the problem domain, and that needs to be done in user code.
If you're finding that you need to mock the file system, I'd say your system-under-test is getting a little too intimate with it. Just delegate those file operations to a handful of higher-level abstractions and exclude their implementations from code coverage, like you would normally do with SQL queries.