Unit Testing – How to Test File Contents Through an Interface in C#

cinterfacesunit testing

I am currently trying to test a file manager class. This class is responsible for keeping track of how long the file is good for. The file will have a date written into it that denotes when it was generated as well as the duration of how long the file is good for. Consumers of my interface will be agnostic to these details. The interface looks like:

public interface IFileManager
{
    bool GenerateFile(string location);
    bool DeleteFile(string location);
    bool IsFileValid(string location);
    bool Update(string customerID, string location);
}

I am concerned with how to test one of these methods, specifically IsValid(string location). Part of the implementation I am planning on for IsValid will be to check whether the file has expired, as well as the file not just containing garbage.

Okay, so here is my conundrum. I always write my unit tests in a separate project, where they only have a reference to an interface library. That is, I test everything through an interface so my unit tests will only know about IFileManager. So from a high-level interface, how do I test a specific implementation of an "expired" or "garbage" file?

My interface does not allow for a client of said interface to create a file that's already expired. And that makes sense to me! On top of that, I don't want to add in functionality to my interface just to be able to test! My thoughts about how can I test this, give me a code smell.

Best Answer

My thoughts about how can I test this, give me a code smell.

This API is encapsulating a side effect. And that's fine -- most of your code doesn't need to know the details. But for unit testing, you probably need a way to replace the side effect with a test double.

You're implementation is probably using a small number of calls to some File library to do the actual work. So the idea is to have an interface that wraps those calls, and pass an instance of that interface in to get the IFileManager you want.

interface FilePrimitives { ... }

IFileManager fileManager(FilePrimitives filePrimitives) { ... }

The is basically the "Strategy" pattern at work.

In your production code, you initialize the FileManager using an implementation of FilePrimitives that makes actual library calls. The primitives are effectively part of the "imperative shell", where your code interacts with the outside world.

Key point: one of your constraints in implementing the primitives is that they should be too simple to break.

When you are testing, instead of using the "real" primitives, you use a test double, and control the responses from the test.

So if you want to test a garbage file, you set up your test double so that it reacts as if there really is a garbage file, and then use that to initialize the rest of your system.

Your test is really two pieces -- it is both a use case (exercising the API) validator, but it is also a composition root.

So you are saying to abstract the file library and then inject a mock version of it that always reports that a file is expired?

Yes, although I would spell it somewhat differently: we're taking the decision to use this file library, and placing a module boundary around that decision, so that we can change that decision in the test harness.

Related Topic