0

I'm trying to use Mocks in my Unit testing but I struggle with below code. I want to Callback from AppendName method in right way so that this method is correctly tested and mocked object name is actually changed.

public class GenericTests
{
   [TestMethod]
   public void TestMethod1()
   {
       IFile newFile = GetNewFile("Document").Object;
       newFile.AppendName();
       Assert.AreEqual("AppendedDocument", newFile.Name);
   }

   public Mock<IFile> GetNewFile(string name)
   {
       Mock<IFile> file = new Mock<IFile>();
       file.Setup(f => f.Name).Returns(name);
       file.Setup(f => f.Path).Returns(@"C:\Folder\" + name);
       file.Setup(f => f.AppendName()).Callback.....problem is here.....

        return file;
   }
}
public interface IFile
{
    string Name { get; set; }
    string Path { get; set; }
    void AppendName();
}
public class LocalFile : IFile
{
    public string Name { get; set; }
    public string Path { get; set; }
    public void AppendName()
    {
        this.Name = "AppendedDocument";
    }
} 

Can someone please advice how to complete Callback for AppendName() ??? please

Richardissimo
  • 5,596
  • 2
  • 18
  • 36
MaciejPL
  • 1,017
  • 2
  • 9
  • 16
  • 1
    Observation: the test you have shown is simply testing a mock object, not actually testing your code, so I hope you're just using that to demonstrate the problem, not as a genuine test. – Richardissimo Sep 15 '18 at 22:23
  • Possible duplicate of [Can you help me understand Moq Callback?](https://stackoverflow.com/questions/2833162/can-you-help-me-understand-moq-callback) – Richardissimo Sep 15 '18 at 22:32
  • Hi, yes this is just an example to show where i my problem :) – MaciejPL Sep 16 '18 at 07:52

1 Answers1

1

You see, interface IFile and class LocalFile are different types. So we can't make from Mock<IFile> reproduce the implementation from LocalFile by default. To make your test work you can add next Callback call:

public Mock<IFile> GetNewFile(string name)
{                
    Mock<IFile> file = new Mock<IFile>();
    file.CallBase = true;
    file.Setup(f => f.Name).Returns(name);
    file.Setup(f => f.Path).Returns(@"C:\Folder\" + name);
    file.Setup(f => f.AppendName()).Callback(() => file.Setup(f => f.Name).Returns(() => 
    {
        var localFile = new LocalFile();
        localFile.AppendName();
        return localFile.Name;
        // or just return "AppendedDocument"
    }));   

    return file;
} 

Example works, however it is not what you need, I suppose. Even for Mock<LocalFile> with CallBase = true and public virtual public string Name { get; set; } you would need to clear property setup somehow. As far as I know Moq doesn't allow this. I can be wrong. You can try the next workaround, based on the same idea:

public class GenericTests
{
    [Test]
    public void TestMethod1()
    {
        IFile newFile = GetNewFile("Document");
        newFile.AppendName();
        Assert.AreEqual("AppendedDocument", newFile.Name);
    }

    public IFile GetNewFile(string name)
    {
        Mock<LocalFile> file = new Mock<LocalFile>();
        file.CallBase = true;
        file.Object.AppendName();
        // remember expected result before setting up the mock
        var appendDoc = file.Object.Name;
        file.Setup(f => f.Name).Returns(name);
        file.Setup(f => f.Path).Returns(@"C:\Folder\" + name);
        file.Setup(f => f.AppendName())
            // change property behavior after call of AppendName 
            .Callback(() => file.Setup(f => f.Name).Returns(appendDoc));
        return file.Object;
    }
}

public interface IFile
{
    string Name { get; set; }
    string Path { get; set; }
    void AppendName();
}

public class LocalFile : IFile
{
    public virtual string Name { get; set; }
    public virtual string Path { get; set; }
    public virtual void AppendName()
    {
        this.Name = "AppendedDocument";
    }
}

I have changed your example a little bit. GetNewFile now returns IFile instance and members of the LocalFile became virtual. Hope it helps.

KozhevnikovDmitry
  • 1,660
  • 12
  • 27