You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I want to be able to have the mock file system to block tasks when writing to some streams, and sometime fails mid-writing streams. Plus all sorts of other things i wont to control.
It would be really easy to do if this awesome test library followed the open-closed SOLID principle.
Solution
Use virtual methods and properties so we can override default mocking behavior (or just extend it).
Issue-Label Bot is automatically applying the label type: enhancement to this issue, with a confidence of 0.86. Please mark this comment with 👍 or 👎 to give our bot feedback!
I have had to resort to using moq as well, which is not easy to do because you can’t just switch in a moq for 1 part.
Virtual methods and properties on the test implementations members that implement the interface would be simplist to achieve extensibility for testing IMO.
Thanks for your feedback. As mentioned in the linked issue I'm hesitant against making stuff virtual too quickly. Could you provide some code samples that you think are currently harder than necessary?
Sure, what is your guide to know when something should be virtual?
IMO I think making something virtual that is implementing a public interface method/property is relatively harmless.
Making a method/property that is not part of a public interface (protected / some other public members) is dangerous. It exposes internal implementation detail for extension which is risky to do for a public library like this.
Here is an example for extend the file system with moq:
public class MoqFileSystem : IFileSystem, IMockFileDataAccessor
{
private readonly MockFileSystem _mockedFileSystem;
public MoqFileSystem()
{
var currentDirectory = System.IO.Directory.GetCurrentDirectory();
_mockedFileSystem = new MockFileSystem(new Dictionary<string, MockFileData>(), currentDirectory);
MockFile = new Mock<MockFile>(this) {CallBase = true};
MockDirectory = new Mock<MockDirectory>(this, currentDirectory) { CallBase = true };
MockFileInfo = new Mock<MockFileInfoFactory>(this) { CallBase = true };
MockFileStream = new Mock<MockFileStreamFactory>(this) { CallBase = true };
MockPath = new Mock<MockPath>(this) { CallBase = true };
MockDirectoryInfo = new Mock<MockDirectoryInfoFactory>(this) { CallBase = true };
MockDriveInfo = new Mock<MockDriveInfoFactory>(this) { CallBase = true };
MockFileSystemWatcher = new Mock<MockFileSystemWatcherFactory>(this) { CallBase = true };
}
public Mock<MockFile> MockFile { get; }
public Mock<MockDirectory> MockDirectory { get; }
public Mock<MockFileInfoFactory> MockFileInfo { get; }
public Mock<MockFileStreamFactory> MockFileStream { get; }
public Mock<MockPath> MockPath { get; }
public Mock<MockDirectoryInfoFactory> MockDirectoryInfo { get; }
public Mock<MockDriveInfoFactory> MockDriveInfo { get; }
public Mock<MockFileSystemWatcherFactory> MockFileSystemWatcher { get; }
#region IFileSystem implementation
public IFile File => MockFile.Object;
public IDirectory Directory => MockDirectory.Object;
public IFileInfoFactory FileInfo => MockFileInfo.Object;
public IFileStreamFactory FileStream => MockFileStream.Object;
public IPath Path => MockPath.Object;
public IDirectoryInfoFactory DirectoryInfo => MockDirectoryInfo.Object;
public IDriveInfoFactory DriveInfo => MockDriveInfo.Object;
public IFileSystemWatcherFactory FileSystemWatcher => MockFileSystemWatcher.Object;
#endregion
#region IMockFileDataAccessor implementation
public IFileSystem FileSystem => this;
public PathVerifier PathVerifier => _mockedFileSystem.PathVerifier;
public MockFileData GetFile(string path)
{
return _mockedFileSystem.GetFile(path);
}
public void AddFile(string path, MockFileData mockFile)
{
_mockedFileSystem.AddFile(path, mockFile);
}
public void AddDirectory(string path)
{
_mockedFileSystem.AddDirectory(path);
}
public void AddFileFromEmbeddedResource(string path, Assembly resourceAssembly, string embeddedResourcePath)
{
_mockedFileSystem.AddFileFromEmbeddedResource(path, resourceAssembly, embeddedResourcePath);
}
public void AddFilesFromEmbeddedNamespace(string path, Assembly resourceAssembly, string embeddedResourcePath)
{
_mockedFileSystem.AddFilesFromEmbeddedNamespace(path, resourceAssembly, embeddedResourcePath);
}
public void MoveDirectory(string sourcePath, string destPath)
{
_mockedFileSystem.MoveDirectory(sourcePath, destPath);
}
public void RemoveFile(string path)
{
_mockedFileSystem.RemoveFile(path);
}
public bool FileExists(string path)
{
return _mockedFileSystem.FileExists(path);
}
public IEnumerable<string> AllPaths => _mockedFileSystem.AllPaths;
public IEnumerable<string> AllFiles => _mockedFileSystem.AllFiles;
public IEnumerable<string> AllDirectories => _mockedFileSystem.AllDirectories;
public StringOperations StringOperations => _mockedFileSystem.StringOperations;
#endregion
}
I actually wrote a new file sytem that decorates the MockFile system in a similar way but not with moq, rather its a Fake that allows us to control things like hanging async file writes/reads.
The example above, and the fake, would be much better off with virtual methods - we just want to extend the default mock implementation.
Here is an example for extend the file system with moq:
Thanks a lot! So I assume the only thing that's missing to make your scenario a bit easier is to make the properties of MockFileSystem virtual? All other Mock* types should already have everything virtual because they derive from the *Base classes.
I like what you wrote on virtuals - totally agree with each point.
Now I think about it - overriding parts of "Default" implementation that provides a base class is a bit odd: its troublesome because you don't know which combinations of things to override, and whether or not to continue to call the base implementation. Especially if the component is stateful.
In all the cases I want to extend this library for, I essentially need hooks for certain operations to run extra code - in additional to the default behaviour. The decorator pattern on certain components would help here - however its not easy to do because the mock file system closes off the construction of all of the mocked components.
Here is a strawman idea (without really thinking about it too hard): maybe if there was a way to make the mock file system construction extensible. Like some kind of factory method, or builder pattern. It might even pass a default instance that could be decorated. I need to provide an example but its too late in the night :p
Here is a strawman idea (without really thinking about it too hard): maybe if there was a way to make the mock file system construction extensible. Like some kind of factory method, or builder pattern. It might even pass a default instance that could be decorated. I need to provide an example but its too late in the night :p
👍 I think something like #442 (and also #168) would work out pretty well.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Activity
issue-label-bot commentedon Jun 2, 2020
Issue-Label Bot is automatically applying the label
type: enhancementto this issue, with a confidence of 0.86. Please mark this comment with 👍 or 👎 to give our bot feedback!Links: app homepage, dashboard and code for this bot.
fgreinacher commentedon Jun 12, 2020
Thanks for your suggestion! I think this is very similar to #288, right?
brooknovak commentedon Jun 12, 2020
Yes it is the same request essentially.
I have had to resort to using moq as well, which is not easy to do because you can’t just switch in a moq for 1 part.
Virtual methods and properties on the test implementations members that implement the interface would be simplist to achieve extensibility for testing IMO.
fgreinacher commentedon Jun 16, 2020
Thanks for your feedback. As mentioned in the linked issue I'm hesitant against making stuff virtual too quickly. Could you provide some code samples that you think are currently harder than necessary?
brooknovak commentedon Jun 18, 2020
Sure, what is your guide to know when something should be virtual?
IMO I think making something virtual that is implementing a public interface method/property is relatively harmless.
Making a method/property that is not part of a public interface (protected / some other public members) is dangerous. It exposes internal implementation detail for extension which is risky to do for a public library like this.
Here is an example for extend the file system with moq:
I actually wrote a new file sytem that decorates the MockFile system in a similar way but not with moq, rather its a Fake that allows us to control things like hanging async file writes/reads.
The example above, and the fake, would be much better off with virtual methods - we just want to extend the default mock implementation.
fgreinacher commentedon Jun 20, 2020
See https://github.com/fgreinacher/opinions/blob/master/tech/virtuals-in-csharp.md (your questions actually was the trigger for me to finally write this down, thanks 👍)
Thanks a lot! So I assume the only thing that's missing to make your scenario a bit easier is to make the properties of
MockFileSystemvirtual? All otherMock*types should already have everything virtual because they derive from the*Baseclasses.brooknovak commentedon Jun 21, 2020
I like what you wrote on virtuals - totally agree with each point.
Now I think about it - overriding parts of "Default" implementation that provides a base class is a bit odd: its troublesome because you don't know which combinations of things to override, and whether or not to continue to call the base implementation. Especially if the component is stateful.
In all the cases I want to extend this library for, I essentially need hooks for certain operations to run extra code - in additional to the default behaviour. The decorator pattern on certain components would help here - however its not easy to do because the mock file system closes off the construction of all of the mocked components.
Here is a strawman idea (without really thinking about it too hard): maybe if there was a way to make the mock file system construction extensible. Like some kind of factory method, or builder pattern. It might even pass a default instance that could be decorated. I need to provide an example but its too late in the night :p
fgreinacher commentedon Jun 23, 2020
👍 I think something like #442 (and also #168) would work out pretty well.
github-actions commentedon Aug 23, 2020
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.