1

I have a Class library that does a lot of File IO. Its a bit difficult to test so I wanted to start using the System.IO.Abstractions package. It has an interface that you can implement with either the real file system, or a mocked one.

So when the code is running in production I want the real file system but when testing I want to mock it. My class doing IO stuff looks like this.

private readonly IFileSystem _fileSystem;

public Service(){
   _fileSystem = new FileSystem();  //In test we want Mock file system here
}

internal bool Run(){
   string[] sourceFilePaths = _fileSystem.Directory.GetFiles(_sourceDirectory, "*.xml", SearchOption.AllDirectories);
}

Now I want to use Dependency Injection in order to populate the _fileSystem instance in order to determine what to use. The issue is that I do not know how to do this in a Class Library. I have found tutorials but it seems that the consumer of the library have to do the injection. My problem is that this class library is packaged and uploaded to a platform. This platform cannot do anything with the package before using it. So somehow the library has to figure out itself what instance to use.

One way I can think of is using the debug notation,

#if DEBUG  #endif

But of course its messy and spreads the configuration around.

What is the proper way to use DI in a Class Library?

Liam
  • 22,818
  • 25
  • 93
  • 157
ourly6
  • 13
  • 3
  • There's no difference in using DI in a class library as anything else. Are you using a DI framework, for example (the excellent) [simple injector](https://simpleinjector.org/)? It's very unclear what your actual question is. Do you inject `IFileSystem` into the constructor? How do you resolve your dependencies? – Liam Jul 15 '20 at 07:04
  • Currently I just create it in the constructor yes, I edited my question. – ourly6 Jul 15 '20 at 07:08
  • Don't do the ```new``` anywhere. Inject the dependecy as a parameter of the constructor. – Alpha75 Jul 15 '20 at 07:15
  • Sure I can do constructor injection but the caller should not be responsible I think. – ourly6 Jul 15 '20 at 07:15
  • 1
    In normal execution, a DI framework will inject the dependency for you. When you do the tests you can do the ```new Service(myMock)``` passing your mock as a parameter. – Alpha75 Jul 15 '20 at 07:18
  • [This blog post from Mark Seemann](https://blog.ploeh.dk/2014/05/19/di-friendly-library/) has the answer to your question. – Steven Jul 15 '20 at 08:26

1 Answers1

4

This is your problem:

public Service(){
   _fileSystem = new FileSystem();  //In test we want Mock file system here
}

Your missing the injection part of dependency injection. This should look like this:

public Service(IFileSystem fileSystem){
   _fileSystem = fileSystem;  //In test we want Mock file system here
}

Why?

Now your class now longer a has a hard dependency on FileSystem, it now has a soft dependency on the interface for FileSystem, (IFileSystem).

Now when testing this class your can mock the interface:

//example using nsubstitue (I prefer moq but I've been using this lately)
var mockIFileSystem = Substitute.For<IFileSystem>();
Service testInstance = new Service(mockIFileSystem);

In production code you now have two choices. You could just inject all the dependencies yourself:

Service testInstance = new Service(new FileSystem);

this becomes clunky fast. Or you could use a injection framework (I recommend SimpleInjector though this is just my opinion):

Dependency injection framework (with simple injector)

With a DI framework you register all your dependencies and allow the framework to resolve them for you:

var container = new SimpleInjector.Container();

// Registrations here
container.Register<IFileSystem, FileSystem>();
container.Register<IService, Service>();

// Request instance
IService services = container.GetInstance<IService>();

Note: I never create an instance of IFileSystem above, the DI framework resolves it for me.

for more information I'd suggest you have a look at similar questions, such as What is dependency injection?

Class libraries

There is nothing special about class libraries here. All you need to do is ensure that the dependency registration is called and that any consumer gets the initial dependency from your registration, or allow them to inject their own registration. There are multiple patterns to this and the correct solution depends on your goals.

Liam
  • 22,818
  • 25
  • 93
  • 157
  • I understand, my questions was actually how to actually register the dependencies like you showed here. :) . I was looking at this blog: https://mcguirev10.com/2018/01/31/net-core-class-library-dependency-injection.html and used the Microsoft.Extensions.DependencyInjection. but wasn't sure how to get the IFileSystem back from a IServiceCollection – ourly6 Jul 15 '20 at 07:25
  • This is how you would get `IFileSystem` back https://stackoverflow.com/a/50664810/542251. `serviceProvider.GetService();` – Liam Jul 15 '20 at 07:26
  • But then where do I get the IServiceProvider? – ourly6 Jul 15 '20 at 07:30
  • Click the link and see! – Liam Jul 15 '20 at 07:31
  • this looks like a better explanation https://stackoverflow.com/a/32461714/542251 – Liam Jul 15 '20 at 07:32
  • 1
    Aha I see, I can now use the BuildServiceProvider() to create one. Thanks for all your help! – ourly6 Jul 15 '20 at 07:35
  • Also @ourly6 be wary of the [Service locator anti-pattern here](https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). Ideally you want to instanciate one entry class and have DI handle the rest. If you start adding `serviceProvider.GetService` everywhere you'll soon end up with hard to maintain and hard to test code. – Liam Jul 15 '20 at 08:41