Search code examples
c#moq

How to use a mocked object in a Moq callback (or other method)?


I'm mocking the following interface:

public interface IVirtualFile
{
    string Contents { get; set; }
    // ...
    void SaveToDisk();
    // ...
}

To test this class:

public class InformationHandler
{
    // ...
    public void PrepareAndSaveSpecificInfo(IVirtualFile p_objSaveFile)
    {
        // ...
        p_objSaveFile.Contents = z_strBeginning;
        // ...
        p_objSaveFile.Contents = String.Concat(p_objSaveFile.Contents, z_strOtherInformation);
        // ...
        p_objSaveFile.Contents = String.Concat(p_objSaveFile.Contents, z_strEnd);
        p_objSaveFile.SaveToDisk();
    }
    // ...
}

With this:

[TestFixture]
public TestInformationHandler
{
    private Mock<IVirtualFile> _mockVirtualFile { get; set; }

    [OneTimeSetUp]
    public void CreateMocks()
    {
        // ...
        _mockVirtualFile = new Mock<IVirtualFile>();

        _mockVirtualFile.Setup(vf => vf.SaveToDisk()).Callback(() => CheckSavedData());
        // ...
    }

    private void CheckSavedData()
    {
        // ...
    }    

    [Test]
    public void Test_PrepareAndSaveSpecificInfo()
    {
        // ...
        PrepareAndSaveSpecificInfo(_mockVirtualFile);
        // ...
    }    
}

The thing is, I want CheckSavedData() to use Assert to test the Contents property of my mock object.

So how can I pass said mock object to the method? Or should I make mockVirtualFile a property of TestVirtualFile and use it in CheckSavedData()?

EDIT: as mentioned in the comments to vhr's answer, what I was thinking of isn't feasible, simply because a mock object's Object property's properties don't keep the values set.

To make it even clearer, step-by-step debugging indicates that right after

_mockVirtualFile.Contents = "abcdef";

the property _mockVirtualFile.Contents still returns null.


Solution

  • By using 'Setup', you are mocking the 'SaveToDisk' method behavior which means that you are telling Moq to execute the callback each time you call your object's method from test:

    private Mock<IVirtualFile> mockVirtualFile;
    
    [OneTimeSetUp]
    public void CreateMocks()
    {
        // ...
        mockVirtualFile = new Mock<IVirtualFile>();
    
        mockVirtualFile.Setup(vf => vf.SaveToDisk()).Callback(() => CheckSavedData());
        // ...
    }
     
     [Test]
     public void TestMethod()
     {
          var mockedFile = mockVirtualFile.Object;
          mockedFile.SaveToDisk(); //this will call CheckSavedData() method
          ...
     }
    

    so if you want to do any assertion you can just do e.g:

    mockedFile.SaveToDisk();
    Assert.True(mockedFile.Contents == null)
    

    As the Moq framework is used to 'mock' some behavior you should actually modify your 'Contents' property inside your mocked method otherwise there is no point in asserting the 'Contents' as it will be always null...