2

I‘m working on making a legacy code base more testable and made good progress with (constructor)-injecting dependencies to classes.

I noticed something in all classes that access the file system: they make calls to C#'s static helper classes like System.IO.File or System.IO.Directory. To really be able to unit test these classes I need to mock the calls to those methods as well.

Is this a common practice? I would write an interface like IFileAbstraction which could e.g. include the method definition void WriteAllText(string path, string content) which would then be implemented by a Mock object during unit-testing. During normal execution, I imagine a class that just routes the calls to the corresponding helper class (in this case System.IO.File#WriteAllText(string path, string content).

Is there a better way to achieve testability of file-system accessing classes?

Small note: I use Autofac as a DI container, so it is pretty easy to handle mutiple injected dependencies in my refactored classes.

1

2 Answers 2

6

You should not start to mock out IO because you think you need to follow religiously some some unit testing dogma found in some book. You mock them out when you notice some real issues with the IO code in your tests, which could be

  • your tests become so slow that you cannot run them as frequently as you would like to

  • you cannot easily make the original IO code access the specific files and folders with your test data

So the only "best practice" here is not stick to some cargo cult, but write the test which needs to be written, regardless whether it is a unit test in the strict sense or not.

Assumed you notice one of the above issues, the approach scetched in your question is fine, though the details may differ depending on how many IO methods need to be called, if they depend on each other, if they just read data or create data and folder structures. If only performance is an issue, and you can choose the storage location freely and don't need much persisted test data, you may also consider to use some Ram based file system for testing.

Recommended read: The Way of Testivus - Less Unit Testing Dogma - More Unit Testing Karma

0

You don't mock the calls to static library methods, that would defeat the purpose of the test. You inject file system paths to known locations with known content so you can tell whether you file processing went according to plan.

7
  • Maybe at some point I want to that but up until now (in my whole career), I followed the „puristic“ unit test mantra cited in this answer. It stated that file IO during unit tests is not a „unit test“ any more. Commented Sep 8, 2022 at 9:03
  • From the test’s perspective, what’s the difference between mocking the static call, or supplying a path with known content? Don’t both achieve the same thing? Commented Sep 8, 2022 at 9:28
  • 1
    "that would defeat the purpose of the test" - not if your test isn't trying to test whether or not the library method works. If you're just testing the flow of the logic (where you fully control inputs, and check what gets called when, etc), you can factor your code so that in the corresponding test you don't need to read an actual file. Might not always be worth the effort, though. Commented Sep 8, 2022 at 9:50
  • @Der_Reparator There's a typo in your link, it doesn't work - looks like you were referencing this Commented Sep 8, 2022 at 9:57
  • @Filip yes, that‘s the answer I wanted to reference! In my specific use case, yes maybe I could use „unit test“ more broadly and let the test create a writable temp directory and afterwards check the file‘s content. But to keep the question more general: is the abstracting of a language‘s static helper classes something that‘s feasible to achieve better testability? Commented Sep 8, 2022 at 10:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.