I am trying to figure out how or if it is possible to do the following with Moq
public class Download
{
private IFoo ifoo;
public Download(IFoo ifoo)
{
this.ifoo = ifoo;
}
public void Download()
{
var files = Directory.GetFiles("filepath"); //<<<===
foreach (var item in files)
{
// do something
}
}
}
In unit test.
// Arrange
var mockFoo = new Mock<IFoo>();
mockFoo.setup( s => s.Bar()).returns(true);
var foo = new Foo(mockFoo.Object);
// Act
foo.Download()
How can I mock the files
variable, so the method uses the mock version. Is this even the correct approach? As I am not mocking the class, and rather mocking the dependency how do I go about settings the files variable so it looks at mocked file string[].
Scoped modules (also known as scoped packages) can be mocked by creating a file in a directory structure that matches the name of the scoped module. For example, to mock a scoped module called @scope/project-name , create a file at __mocks__/@scope/project-name.
One option is to create a fake object that emulates the file system with an in-memory representation of it. The other possibility is to set up a test spy/mock for each test method. Considering the initial code snippet, both approaches are inappropriate. Let's see how dependency injection can help us.
'Setup' mocks a method and 'Returns' specify what the mocked method should return.
You would need to depend on an abstraction to get your files instead of having a hard dependency on System.IO.Directory
:
public interface IFileProvider
{
string[] GetFiles(string path);
}
public class PhysicalFileProvider : IFileProvider
{
public string[] GetFiles(string path)
{
return Directory.GetFiles(path);
}
}
You would inject the abstraction in exactly the same way as you're injecting IFoo
.
Now you can mock IFileProvider
using Moq, creating a mock that returns exactly the strings that you want it to return.
var fileProvider = new Mock<IFileProvider>();
fileProvider.Setup(x => x.GetFiles(It.IsAny<string>()))
.Returns(new[] {"file1.txt", "file2.txt"});
You can also use Microsoft.Extensions.FileProviders.Physical which provides both the file system access and the abstraction.
public class Download
{
private readonly IFoo _foo;
private readonly Microsoft.Extensions.FileProviders.IFileProvider _fileProvider;
public Download(IFoo foo, IFileProvider fileProvider)
{
_foo = foo;
_fileProvider = fileProvider;
}
public void SomethingWithFiles()
{
var files = _fileProvider.GetDirectoryContents("filepath")
.Where(item => !item.IsDirectory);
foreach (var item in files)
{
// something
}
}
}
The concrete implementation would be PhysicalFileProvider
.
One more variation. Instead of injecting an interface, inject a delegate:
public delegate string[] GetFilesFunction(string path);
public class Download
{
private readonly IFoo _foo;
private readonly GetFilesFunction _getFiles;
public Download(IFoo foo, GetFilesFunction getFiles)
{
_foo = foo;
_getFiles = getFiles;
}
...
That's even easier to mock. You don't even need Moq.
var subject = new Download(mockedFoo, path => new []{"file1.txt","file2.txt"} );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With