How to mock Directory.getFiles at the same time make sure the production code run as well. For example, if run the unit test it will run the mock data, if in runtime production and actually have parameter has passed it calls the System.io.Directory.
-
If the question I linked doesn't answer yours, please tag me (@john) in a comment and I'll reopen yours. – ProgrammingLlama Jul 03 '19 at 08:17
-
@John it only answer how to do the unit test but didnt show if run the code as normal and actually run into directory and get the files – MAN CHUN LIEW Jul 03 '19 at 08:19
-
If you're talking about the `System.IO.Abstractions` library, it seems that there is a `FileSystem` concrete implementation of the `IFileSystem` interface. Is that sufficient for you? – ProgrammingLlama Jul 03 '19 at 08:20
-
can roughtly show me how to make it? :( – MAN CHUN LIEW Jul 03 '19 at 08:33
-
I don't have time at the moment, so I've re-opened your question, but I'll try to this evening :-) – ProgrammingLlama Jul 03 '19 at 08:40
-
@John try whole week already :( Maybe too lack of the mocking knowledge. After unit test can run found the normal way cant run, if normal can work unit test cant work – MAN CHUN LIEW Jul 03 '19 at 08:44
-
Response to your edit: assuming you pass `ExceptionService` (as `IException`) to `ExceptionEmail`, there's no way that calling `_Exception.GetFiles(path, pattern);` would be any different to `Directory.GetFiles(path, pattern);`. In your unit tests, you can mock `IException` in a similar way to how I mocked `IFileSystem` in my answer. As it stands, it's unclear what problem you're having. – ProgrammingLlama Jul 03 '19 at 11:29
-
@John you mean _Exception.GetFiles(path, pattern); is same to Directory.GetFiles(path, pattern); in my case? – MAN CHUN LIEW Jul 03 '19 at 11:31
-
Essentially, yes. In creating it, you have basically created the `IDirectory` part of System.IO.Abstractions, albeit only for one method. – ProgrammingLlama Jul 03 '19 at 11:35
-
But when Im not running the unit test and run the project as usual it cannot pass though the line _Exception.GetFiles(path, pattern); and I change to Directory it works again. Supposely it should works right no matter in unit test or not the unit test. – MAN CHUN LIEW Jul 03 '19 at 11:40
-
"it cannot pass through the line" - what do you mean? What happens? – ProgrammingLlama Jul 03 '19 at 11:41
-
You aren't using `ExceptionService` in your unit test too, are you? You're mocking `IException` in the unit test, right? – ProgrammingLlama Jul 03 '19 at 11:43
-
It cant find the System.IO.Directory so it cannot get the jsonFileList properly. – MAN CHUN LIEW Jul 03 '19 at 11:44
-
Hold up... it can't find System.IO.Directory?! Can you provide the full error message, please. – ProgrammingLlama Jul 03 '19 at 11:45
-
@John I think you still confuse think i'm in unit test.... my unit test works fine, can mock all the things correctly. But when Im not doing unit test and run project, it just show me the problem cannot get the jsonFileList. – MAN CHUN LIEW Jul 03 '19 at 11:51
-
I give up. You're ignoring the question. 1. You've said unit test works, but running your project doesn't. I wondered if you were modifying `ExceptionService` for unit test instead of mocking it, and then the modified class was obviously failing in production. 2. You've said that it "can't find System.IO.Directory" but won't provide an exact error message, or details. We can't help you if you don't provide enough information. – ProgrammingLlama Jul 03 '19 at 11:51
-
no error message there :( it cannot get the jsonfilelist and directly end the program – MAN CHUN LIEW Jul 03 '19 at 11:54
-
2Provide a [mcve] then. – ProgrammingLlama Jul 03 '19 at 11:57
2 Answers
My recommendation is that you use System.IO.Abstractions, installable from NuGet. You then code to an interface, IFileSystem
, rather than directly to the System.IO File
, Directory
, etc. objects.
So wherever you need to access these methods, you need to inject IFileSystem
, which means an instance of FileSystem
, exposed as IFileSystem
has to be registered with your dependency injection controller, or you need to instantiate your service as new MyService(new FileSystem())
, where there is a constructor parameter which takes IFileSystem
. Note that DI is the preferred way to do this.
Let's create a simple service that returns the files in the current directory:
public class MyService
{
private readonly IFileSystem _fileSystem;
public MyService(IFileSystem fileSystem)
{
this._fileSystem = fileSystem;
}
public string[] GetFileNames()
{
return _fileSystem.Directory.GetFiles(_fileSystem.Directory.GetCurrentDirectory());
}
}
Here you can see that we accept IFileSystem
, which will be injected into our class. Our GetFileNames()
method simply gets the current directory, gets the files in it, and returns them.
Now let's use it in our production code:
// FileSystem should be registered with your dependency injection container,
// as should MyService. MyService should be resolved through the container
// and not manually instantiated as here.
var fileSystem = new FileSystem();
var service = new MyService(fileSystem);
var files = service.GetFileNames();
foreach (var file in files)
{
Console.WriteLine(file);
}
Hey presto, it prints out the expected file listing for the project's build folder.
Now, how do we test it? For the sake of this example, I'm using xUnit and Moq. I have included the System.IO.Abstractions
NuGet package in my unit test project as well.
First, we need to mock the IFileSystem
object:
var mockDirectory = new Mock<IDirectory>();
// set up the GetCurrentDirectory() method to return c:\
mockDirectory.Setup(g => g.GetCurrentDirectory()).Returns(@"c:\");
// Set up the GetFiles method to return c:\test.txt and c:\test2.txt where the path passed is c:\
mockDirectory.Setup(g => g.GetFiles(It.Is<string>(@"c:\"))).Returns(new[] { @"c:\test.txt", @"c:\test2.txt" });
var mockFileSystem = new Mock<IFileSystem>();
// Set up IFileSystem's .Directory property to return the mock of IDirectory that we created above
mockFileSystem.SetupGet(g => g.Directory).Returns(mockDirectory.Object);
// Create an instance of the mock that we can use in our service
var fileSystem = mockFileSystem.Object;
Now to test it we simply need to pass that into the service and call the mehtod:
var myService = new MyService(fileSystem);
var files = myService.GetFileNames();
var expected = new[] { @"c:\test.txt", @"c:\test2.txt" };
Assert.True(files.SequenceEqual(expected));
This will now use our mocked implementation of IFileSystem
in the GetFileNames
method, and thus we can test it. Note that if you're using different overloads of GetFiles
, you'll need to mock the relevant method(s).

- 36,677
- 7
- 67
- 86
-
For reference check https://stackoverflow.com/q/56862061/5233410 and https://stackoverflow.com/questions/56851035/code-doesnt-work-after-rewriting-the-code-for-unit-testing . I'll leave it up to you to decide if you want to continue down this path. – Nkosi Jul 03 '19 at 09:52
-
1Would never suggest not helping. Was simply providing context of OPs previous attempts. – Nkosi Jul 03 '19 at 10:00
-
@Nkosi Sorry I misunderstood what you meant with your previous comment. I should have received the URLs first. Will take a proper look after dinner :) food is important. – ProgrammingLlama Jul 03 '19 at 10:00
-
1
-
@Nkosi Hmm. Hopefully this fairly complete example will clear up the problems OP has been having thus far. – ProgrammingLlama Jul 03 '19 at 10:15
-
1I do not think the issue is with the testing. From my understanding of the problem they are having difficulty understanding how to use the refactored code in production. (outside of unit test) – Nkosi Jul 03 '19 at 10:17
-
1
-
@John I have edit my question and add in more info see whether you understand what Im explain or not. In that case, should i use System.IO.Abstractions? – MAN CHUN LIEW Jul 03 '19 at 11:19
-
@MANCHUNLIEW System.IO.Abstractions is specifically built to allow you to code against an abstraction rather than directly hitting the file system every time. It is perfectly suited to what you're trying to accomplish. – mason Jul 03 '19 at 18:12
-
Here actually i found that problem. Because in my main program I forgot to pass the interface instance into my constructor. – MAN CHUN LIEW Jul 03 '19 at 18:14
You could create your own wrapper class, like
public interface IFileWrapper
{
FileInfo[] GetFiles(DirectoryInfo di);
}
public class FileWrapper : IFileWrapper
{
public FileInfo[] GetFiles(DirectoryInfo di)
{
return di.GetFiles();
}
}
you should be able then to mock out the result.
alternatively there are packages available which may have this already.
hope that helps

- 1,503
- 6
- 25
- 44
-
After the I/O being wrap, can the actual I/O being call when im not doing unit test instead run it as usual and have actual parameter pass thought it and need the system.io.directory to get the file. – MAN CHUN LIEW Jul 03 '19 at 11:22
-
-
@MANCHUNLIEW That' what dependency injection is for. In testing, you'd inject a mock file system. When running in production, you'd inject the actual file system implementation. – mason Jul 03 '19 at 17:47