I have injected the System.IO.Abstractions.IFileSystem interface into a class so that I can unit test file system interactions. There is one place in the class that uses new FileInfo(fileName). What is the replacement for that when using the IFileSystem interface and MockFileSystem?
Replacing File.OpenRead with _fileSystem.File.OpenRead is simple...
public string? Decrypt(string encryptedFilePath, string privateKeyArmor, string passPhrase)
{
try
{
using var privateKeyStream = new MemoryStream(Encoding.ASCII.GetBytes(privateKeyArmor));
using var encryptedFileStream = _fileSystem.File.OpenRead(encryptedFilePath);
var inputStream = PgpUtilities.GetDecoderStream(encryptedFileStream);
...
...but I don't know how to replace new FileInfo(fileName) here.
private byte[] CompressFile(string fileName, CompressionAlgorithmTag algorithm)
{
var outputStream = new MemoryStream();
var compressedDataGen = new PgpCompressedDataGenerator(algorithm);
PgpUtilities.WriteFileToLiteralData(compressedDataGen.Open(outputStream), PgpLiteralData.Binary,
new FileInfo(fileName));
...
I tried _fileSystem.FileInfo.FromFileName(fileName), but that returns IFileInfo instead of FileInfo and the WriteFileToLiteralData method won't take that.
There is a helper function FileInfo.New(string fileName) which can be used to create/use a mock IFileInfo object
public class FileInfoTest
{
private readonly IFileSystem _fileSystem;
public FileInfoTest()
: this (new FileSystem())
{
}
internal FileInfoTest(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public bool GetIsReadOnly(string path)
{
var info = _fileSystem.FileInfo.New(path);
return info.IsReadOnly;
}
}
To demonstrate this I have a physical file which is not read-only.
The first test, returns the IsReadonly state of the physical file.
The second, returns a mocked IFileInfo object with IsReadOnly set to true.
[TestMethod]
public void CheckFileInfoAgainstPhysicalFile()
{
var tester = new FileInfoTest();
var isReadOnly = tester.GetIsReadOnly(#"c:\dev\File.txt");
Assert.IsFalse(isReadOnly);
}
[TestMethod]
public void CheckFileInfoAgainstMock()
{
var mockFileInfo = new Mock<IFileInfo>();
mockFileInfo.SetupGet(mk => mk.IsReadOnly).Returns(true);
var mockFileSystem = new Mock<IFileSystem>();
mockFileSystem.Setup(mk => mk.FileInfo.New(#"c:\dev\File.txt")).Returns(mockFileInfo.Object);
var tester = new FileInfoTest(mockFileSystem.Object);
var isReadOnly = tester.GetIsReadOnly(#"c:\dev\File.txt");
Assert.IsTrue(isReadOnly);
}
As mentioned in the comment, the above doesn't address the basic problem - PgpUtilities doesn't know what an IFileInfo is.
There is a way to fix this but it may not be worth the effort.
Define an interface for the methods used from the static class.
Inject this interface.
In the default implementation of `IPgpUtilities`, use reflection to get at the `FileInfo` instance inside the `FileInfoWrapper` and pass it through to the external routine.
// Stand-in for External utility (returns a string so we can see it
// doing something with the original file)
public static class PgpUtilitiesOriginal
{
public static string WriteFileToLiteralData(Stream outputStream,
char fileType,
FileInfo file)
{
return file.Name;
}
}
// Interface for injection
public interface IPgpUtilties
{
string WriteFileToLiteralData(Stream outputStream,
char fileType,
IFileInfo file);
}
// Wrapper for the External Utility
public class DefaultPgpUtilities : IPgpUtilties
{
public string WriteFileToLiteralData(Stream outputStream, char fileType, IFileInfo file)
{
var instanceInfo = file.GetType().GetField("instance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var instance = (FileInfo)instanceInfo.GetValue(file);
return PgpUtilitiesOriginal.WriteFileToLiteralData(outputStream, fileType, instance);
}
}
// Test Target
public class Tester
{
private readonly IFileSystem _fileSystem;
private readonly IPgpUtilties _pgpUtilities;
public Tester()
: this(new FileSystem(), new DefaultPgpUtilities())
{
}
public Tester(IFileSystem fileSystem, IPgpUtilties pgpUtilities)
{
_fileSystem = fileSystem;
_pgpUtilities = pgpUtilities;
}
public string Run(string fileName)
{
return _pgpUtilities.WriteFileToLiteralData(null, '\0', _fileSystem.FileInfo.FromFileName(fileName));
}
}
[TestMethod]
public void PhysicalFile()
{
var tester = new Tester();
var ret = tester.Run(#"c:\dev\file.txt");
Assert.AreEqual("file.txt", ret);
}
[TestMethod]
public void MockedFile()
{
var mockFileObject = new Mock<IFileInfo>();
var mockFileSystem = new Mock<IFileSystem>();
mockFileSystem.Setup(mk => mk.FileInfo.FromFileName(#"c:\dev\file.txt")).Returns(mockFileObject.Object);
var mockPgpUtilties = new Mock<IPgpUtilties>();
mockPgpUtilties.Setup(mk => mk.WriteFileToLiteralData(It.IsAny<Stream>(), It.IsAny<char>(), mockFileObject.Object)).Returns("Hello World");
var tester = new Tester(mockFileSystem.Object, mockPgpUtilties.Object);
var ret= tester.Run(#"c:\dev\file.txt");
Assert.AreEqual("Hello World", ret);
}
Again, sorry about the piss-poor reading of the original question on my part.
Related
I am trying to use NUnit and Moq for writing unit tests for my sample project. I have a service class like below:
SentimentService.cs:
public class SentimentService : ISentimentService
{
private readonly IStreamReader _reader;
private readonly IServerClient _serverClient;
public SentimentService(IStreamReader reader, IServerClient serverClient)
{
_reader = reader;
_serverClient = serverClient;
}
public async Task<string> CalculateSentimentFromTextFile(IFormFile file)
{
var input = "";
using (StreamReader streamReader = _reader.GetReader(file.OpenReadStream()))
{
input = streamReader.ReadToEnd();
streamReader.Close();
}
var result = await _serverClient.PostAsync<SentimentResult, string>($"submit_string", input);
return result.label;
}
}
I tried of using Moq like below:
[Test]
public async Task CalculateSentimentFromTextFileEquality()
{
var mockFormFile = new Mock<IFormFile>();
var mockStreamReader = new Mock<IStreamReader>();
var mockServerClient = new Mock<IServerClient>();
var mockResult = new SentimentResult
{
label = "Positive"
};
mockStreamReader.Setup(o => o.GetReader(mockFormFile.Object.OpenReadStream()));
mockServerClient.Setup(o => o.PostAsync<SentimentResult, string>(It.IsAny<string>(), It.IsAny<string>())).ReturnsAsync(mockResult);
var sut = new SentimentService(mockStreamReader.Object, mockServerClient.Object);
var result = await sut.CalculateSentimentFromTextFile(mockFormFile.Object);
Assert.That(result, Is.EqualTo("Positive"));
}
But, getting the below error:
This setup:
mockStreamReader.Setup(o => o.GetReader(mockFormFile.Object.OpenReadStream()));
does not return any object and thus when calling this in SUT:
StreamReader streamReader = _reader.GetReader(file.OpenReadStream()
will return a null object.
However, StreamReader has parameterless constructors, which means you have to provide the real file path/stream if you want to mock it. One way to handle the StreamReader is to create an interface which later can be implemented by a wrapper of StreamReader.
public interface IStreamReader : IDisposable
{
string ReadToEnd();
void Close();
}
Creation of IStreamReader is made by a factory:
public interface IStreamReaderFactory
{
IStreamReader GetReader(Stream stream);
}
Your SUT can be modified to:
public class SentimentService : ISentimentService
{
private readonly IStreamReaderFactory _readerFactory;
private readonly IServerClient _serverClient;
public SentimentService(IStreamReaderFactory readerFactory, IServerClient serverClient)
{
_readerFactory = readerFactory;
_serverClient = serverClient;
}
public async Task<string> CalculateSentimentFromTextFile(IFormFile file)
{
var input = "";
using (IStreamReader streamReader = _readerFactory.GetReader(file.OpenReadStream()))
{
input = streamReader.ReadToEnd();
streamReader.Close();
}
var result = await _serverClient.PostAsync<SentimentResult, string>($"submit_string", input);
return result.label;
}
}
The test code should now be able to setup IStreamReader properly.
var mockStreamReader = new Mock<IStreamReader>();
var mockStreamReaderFactory = new Mock<IStreamReaderFactory>();
mockStreamReaderFactory.Setup(o => o.GetReader(It.IsAny<Stream>())).Returns(mockStreamReader.Object);
I want to save the information of a model in the Json file I want to implement this in such a way that I can easily store different models So I used this method:
public abstract class SettingBase
{
[JsonIgnore]
public abstract string Filename { get; }
public static T Load<T>() where T : SettingBase, new()
{
T result = new T();
result = JsonFile.Load<T>(result.Filename) ?? result;
return result;
}
public void Save()
{
JsonFile.Save(Filename, this);
}
}
JsonFile:
public static class JsonFile
{
public static void Save<T>(string fileName, T #object)
{
using (StreamWriter writer = File.CreateText(fileName))
{
string json = JsonSerializer.Serialize(#object);
writer.Write(json);
}
}
public static T Load<T>(string fileName)
{
using (StreamReader reader = File.OpenText(fileName))
{
string json = reader.ReadToEnd();
return JsonSerializer.Deserialize<T>(json);
}
return default(T);
}
}
now i can use like this:
var xx = SettingBase.Load<AppModel>();
xx.boo = true;
xx.integ = 25;
xx.str = "sss";
xx.Save();
The problem is that the save operation is not performed and nothing is saved in the json file
How to solve this problem?
Unfortunately, this feature is not currently supported on .NET Core Probably supported in .Net 6.0 You must use newtonsoft.json
public static void Save<T>(string fileName, T #object)
{
var json = JsonSerializer.Serialize(#object);
System.IO.File.WriteAllText(fileName, json);
}
public static T Load<T>(string fileName)
{
var json = System.IO.File.ReadAllText(fileName);
return JsonSerializer.Deserialize<T>(json)
}
I can't get Moq to mock an object that gets created in a static method.
Here is my moq and code
code:
public interface IConfigHelper
{
string GetConfiguration(string sectionName, string elementName);
}
public class ConfigHelper : IConfigHelper
{
public ConfigHelper() { }
public virtual string GetConfiguration(string sectionName, string elementName)
{
string retValue = String.Empty;
//Does things to get configuration and return a value
return retValue;
}
}
public class myRealClass
{
public myRealClass(){}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = utilSvc.GetConfigurationValue();
return retValue;
}
}
public static class utilSvc
{
public static string GetConfigurationValue()
{
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
the Test using Moq
[TestFixture(TestName = "Tests")]
public class Tests
{
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
}
[Test]
public void serviceIsBPManagementForValidSource()
{
//Arrange
string sectionName = "sectionName/sectionElement";
string clinicalElementName = "ClinicalSystem";
string clinicalElementValue = "Zedmed";
configHelperMOCK.Setup(s => s.GetConfiguration(sectionName, clinicalElementName)).Returns(clinicalElementValue);
//act
// the call to myRealClass
//assert
// test assertions
}
}
The issue that I am having is with this line:
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
I cannot get the moq to Mock the object.
I do not want the code to read the config file. I wish to moq away this instance of ConfigHelper
You can't wrap the static class/method but you can redirect it
public static class UtilSvc
{
static UtilSvc()
{
CreatorFunc = () => new ConfigHelper();
}
public static Func<IConfigHelper> CreatorFunc { get; set; }
public static string GetConfigurationValue()
{
var configUtil = CreatorFunc();
return configUtil.GetConfiguration("sectionName/sectionElement",
"ClinicalSystem");
}
}
and then in the test
//...
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
UtilService.CreatorFunc = () => configHelperMOCK.Object;
}
//...
You cannot mock static class. I would rather propose to inject that IConfigHelper into the myRealClass. That is the usual way how to decouple dependencies and use DI.
public class myRealClass
{
private IConfigHelper _configHelper;
public myRealClass(IConfigHelper configHelper)
{
_configHelper = configHelper;
}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = _configHelper.GetConfigurationValue();
return retValue;
}
}
Avoid coupling your code to static classes, which in most cases cause you code be to difficult to maintain and test.
Follow the Explicit Dependencies Principle
Methods and classes should explicitly require (typically through
method parameters or constructor parameters) any collaborating objects
they need in order to function correctly.
Give the article a read. It is short and very informative.
If you want to keep the static class then you wrap the static class behind an abstraction.
public interface IUtilSvc {
string GetConfigurationValue();
}
public class utilSvcWrapper : IUtilSvc {
public string GetConfigurationValue() {
return utilSvc.GetConfigurationValue(); //Calling static service
}
}
Or another option is that utlSvc does not have to be static if can be injected into dependent classes
public class utilSvc : IUtilScv {
private readonly IConfigHelper configUtil;
public utilSvc(IConfigHelper configHelper) {
configUtil = configHelper;
}
public string GetConfigurationValue() {
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
Inject the IUtilScv into the dependent class so that it is no longer dependent on static class.
public class myRealClass {
private readonly IUtilScv utilSvc;
//Explicit dependency inject via constructor
public myRealClass(IUtilScv utilSvc) {
this.utilSvc = utilSvc;
}
public string myworkingMethod() {
var retValue = utilSvc.GetConfiguration();
return retValue;
}
}
In that case you don't even need IConfigHelper when testing as it has also been abstracted away. And you only need to mock the dependencies needed for the test.
[TestFixture(TestName = "Tests")]
public class Tests {
private Mock<IUtilScv> utilScvMOCK;
[SetUp]
public void Setup() {
utilScvMOCK = new Mock<IUtilScv>();
}
[Test]
public void serviceIsBPManagementForValidSource() {
//Arrange
var expectedClinicalElementValue = "Zedmed";
utilScvMOCK
.Setup(s => s.GetConfiguration())
.Returns(expectedClinicalElementValue)
.Verifiable();
var sut = new myRealClass(utilScvMOCK.Object);
//Act
var actualClinicalElementValue = sut.myworkingMethod();
//Assert
configHelperMOCK.Verify();
Assert.AreEqual(expectedClinicalElementValue, actualClinicalElementValue);
}
}
I am trying to mock a delegate, using Moq, but so far everything im doing is in vain.
I have made a small example of how the setup is:
1.A proxy class is abstracting a WCF service
public interface IServiceProxy
{
void FooService(ServiceProxy.FooServiceDelegate service);
}
public class ServiceProxy : IServiceProxy
{
private readonly EndpointAddress _fooServiceEndpoint;
public ServiceProxy(IConfiguration configuration)
{
_fooServiceEndpoint = new EndpointAddress(new Uri(configuration.WcfServiceEndpoint));
}
public delegate void FooServiceDelegate(IFooBar proxy);
public void FooService(FooServiceDelegate service)
{
Do<IFooBar>((service.Invoke), _fooServiceEndpoint);
}
private void Do<T>(UseServiceDelegate<T> f, EndpointAddress address)
{
UsingService<T>.Use(f.Invoke, address);
}
}
2.The service definition
/// <summary>
/// This is the interface the WCF service exposes
/// </summary>
public interface IFooBar
{
string Hello(string name);
}
3.And the class that uses the proxy
public class DoFoo
{
private readonly IServiceProxy _serviceProxy;
public DoFoo(IServiceProxy serviceProxy)
{
_serviceProxy = serviceProxy;
}
public string SayHello(string name)
{
var response = string.Empty;
_serviceProxy.FooService(proxy => { response = proxy.Hello(name); });
return response;
}
}
I would like to test that the methods defined on the delegate FooServiceDelegate with the input IFooBar is in fact called and also i would like to be able to mock the responce from a methodcall on a IFooBar via the delegate.
So far my attempts has been in vain, so i hope that there are some moq experts that can help here.
Here is an example of a test, that of course does not work as it is now.
[TestClass()]
public class DoFooTests
{
[TestMethod, TestCategory("Unit")]
public void SayHelloJohn_ShouldUseServiceProxy()
{//SETUP
var serviceMock = new Mock<IFooBar>(MockBehavior.Loose);
var delegateMock = new Mock<ServiceProxy.FooServiceDelegate>(MockBehavior.Strict);
var serviceProxyMock = new Mock<IServiceProxy>(MockBehavior.Strict);
serviceProxyMock.Setup(m => m.FooService(delegateMock.Object));
var name = "John";
var response = $"Hello {name}";
//Im trying something like this (of course this does not work...)
delegateMock.Setup(m => m.Hello(name)).Returns(response);
//EXECUTE
var doFoo = new DoFoo(serviceProxyMock.Object);
var result = doFoo.SayHello(name);
//ASSERT
// Do some assertions here....
}
}
You have to call the delegate when FooService is executed:
[TestMethod]
public void SayHelloJohn_ShouldUseServiceProxy()
{
const string EXPECTED_RESULT = "Hello John";
const string NAME = "John";
var fakeServiceProxy = new Mock<IServiceProxy>(MockBehavior.Strict);
var fakeFooBar = new Mock<IFooBar>(MockBehavior.Loose);
fakeFooBar.Setup(bar => bar.Hello(NAME)).Returns(EXPECTED_RESULT);
fakeServiceProxy.Setup(proxy => proxy.FooService(
It.IsAny<ServiceProxy.FooServiceDelegate>()))
.Callback<ServiceProxy.FooServiceDelegate>(arg => arg(fakeFooBar.Object));
var target = new DoFoo(fakeServiceProxy.Object);
var result = target.SayHello(NAME);
Assert.AreEqual(EXPECTED_RESULT, result);
}
The above snippet execute proxy => { response = proxy.Hello(name); }) when you call SayHello method.
I have the following unit test:
[TestClass]
public class DirectoryWatcherTests
{
private AutoMoqer _mocker;
private DirectoryWatcher _instance;
[TestInitialize]
public void Setup()
{
_mocker = new AutoMoqer();
_instance = _mocker.Create<DirectoryWatcher>();
}
[TestMethod]
public void Watcher_Gets_Path_Set_Same_As_Begin_Path_Parameter()
{
const string path = #"C:\test";
_instance.Begin(path);
_mocker.GetMock<FileSystemWatcherBase>()
.VerifySet(x => x.Path = path);
}
}
The code i wrote to get this to pass was:
public class DirectoryWatcher
{
private readonly FileSystemWatcherBase _fileSystemWatcher;
public DirectoryWatcher(FileSystemWatcherBase fileSystemWatcher)
{
_fileSystemWatcher = fileSystemWatcher;
}
public void Begin(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException("FileSystemWatcher passed in without a valid path already set");
_fileSystemWatcher.Path = path;
}
}
However, the VerifySet fails with:
Expected invocation on the mock at least once, but was never performed: x => x.Path = "C:\test"
Why is it claiming the setter is never being called? If it helps at all FileSystemWatcherBase is an abstract class.
Thanks to Eugene I have figured out that this seems to be an issue with Automoq and its compatibility with the latest version of Unity. I created the following tests to prove that it's an Automoq issue and not a Moq issue:
[TestMethod]
public void Test()
{
const string path = #"C:\test";
var watcherMock = new Mock<FileSystemWatcherBase>();
watcherMock.Object.Path = path;
watcherMock.VerifySet(x => x.Path = path);
}
[TestMethod]
public void Test2()
{
const string path = #"C:\test";
var mocker = new AutoMoqer();
var instance = mocker.Create<Tester>();
var watcherMock = mocker.GetMock<AbstractTest>();
watcherMock.Object.Path = path;
watcherMock.VerifySet(x => x.Path = path);
}
[TestMethod]
public void Test3()
{
const string path = #"C:\test";
var mocker = new AutoMoqer();
var instance = mocker.Create<Tester>();
var watcherMock = mocker.GetMock<AbstractTest>();
instance.Run(path);
watcherMock.VerifySet(x => x.Path = path);
}
[TestMethod]
public void Test4()
{
const string path = #"C:\test";
var testMock = _mocker.GetMock<AbstractTest>();
var tester = new Tester(testMock.Object);
tester.Run(path);
testMock.VerifySet(x => x.Path = path);
}
public abstract class AbstractTest
{
public abstract string Path { get; set; }
}
public class Tester
{
private readonly AbstractTest _test;
public Tester(AbstractTest test)
{
_test = test;
}
public void Run(string path)
{
_test.Path = path;
}
}
Tests 1, 2 and 4 pass while 3 fails. I was able to find a work around the issue via the following test case:
[TestMethod]
public void Test5()
{
const string path = #"C:\test";
var mocker = new AutoMoqer();
var watcherMock = mocker.GetMock<AbstractTest>();
var instance = mocker.Create<Tester>();
instance.Run(path);
watcherMock.VerifySet(x => x.Path = path);
}
Essentially having Automoq get me the mock prior to creating the class I'm trying to test allows the verify to work. This leads me to believe that Automoq does not realize a moq was already created for the tested class and thus a call to GetMock<T> creates a new one.