Mocking contructors (future mocking?)

4 posts, 1 answers
  1. Nacho
    Nacho avatar
    15 posts
    Member since:
    Oct 2013

    Posted 13 Feb 2014 Link to this post

    Hello, 

    I'm trying to test the following code:

    public ICollection<RawCatalog> ReadCatalog(string familyName)
    {
        // Root folder for the family
        string familyFolder = this.GetFamilyFolder(familyName);
        DirectoryInfo familyFolderInfo = new DirectoryInfo(familyFolder);
     
        foreach (DirectoryInfo subFamilyFolderInfo in familyFolderInfo.EnumerateDirectories())
        {
            // Do stuff
        }
    }

    I expected that this would work:

    // Arrange
    DirectoryInfo fakeDirectoryInfo = Mock.Create<DirectoryInfo>(Constructor.Mocked);
    Mock.Arrange(() => new DirectoryInfo(@"testRoot\DrivesData\TestFamily")).Returns(fakeDirectoryInfo);
    Mock.Arrange(() => directoryInfo.EnumerateDirectories()).Returns(new DirectoryInfo[] { });

    But is not working as seems that fakeDirectoryInfo is not being returned in the constructor. How should I do the test? (I should not change the source code as it's working code if possible).

    I've read something about future mocking and using DoNothing() but not sure if this apply to my own situation.

    Thanks in advance.
  2. Kaloyan
    Admin
    Kaloyan avatar
    872 posts

    Posted 17 Feb 2014 Link to this post

    Hello Nacho,

    Thank you for contacting our support system.

    Unfortunately, arranging a return value on a constructor interception is not possible with JustMock.
    (Mock.Arrange(() => new DirectoryInfo(@"testRoot\DrivesData\TestFamily")).Returns(fakeDirectoryInfo);)

    I managed to prepare an example for you, that tests very similar scenario. First, let's assume we have the following system under test:
    public class TestClass
    {
        public string GetFamilyFolder(string familyName)
        {
            return @"testRoot\DrivesData\TestFamily";
        }
     
        public ICollection<DirectoryInfo> ReadCatalog(string familyName)
        {
            // Root folder for the family
            string familyFolder = this.GetFamilyFolder(familyName);
            DirectoryInfo familyFolderInfo = new DirectoryInfo(familyFolder);
     
            var collection = new List<DirectoryInfo>();
            foreach (DirectoryInfo subFamilyFolderInfo in familyFolderInfo.EnumerateDirectories())
            {
                collection.Add(subFamilyFolderInfo);
            }
     
            return (ICollection<DirectoryInfo>)collection;
        }
    }

    Now, to test the ReadCatalog function, we will need to isolate the DirectoryInfo calls. Here is the test method:
    [TestMethod]
    public void TestMethod1()
    {
        // Arrange
        // Defining some parameters for the test.
        var expected = new DirectoryInfo("test");
        var passedString = @"testRoot\DrivesData\TestFamily";
     
        // Instantiating our class under test.
        var sUT = new TestClass();
     
        // This will arrange the constructor of any new DirectoryInfo instance,
        //  that matches the passedString argument, to do nothing.
        Mock.Arrange(() => new DirectoryInfo(passedString)).DoNothing();
     
        // Dummy instance, needed for the future arrange on the next line.
        var directoryInfo = new DirectoryInfo("test");
     
        // Arranging directoryInfo.EnumerateDirectories() to return a fake collection
        //  no matter the instance that is called from. This is also called Future Mocking.
        Mock.Arrange(() => directoryInfo.EnumerateDirectories())
            .IgnoreInstance()
            .Returns(new DirectoryInfo[] { expected });
     
        // Act
        var actual = sUT.ReadCatalog(passedString);
     
        // Assert
        Assert.AreEqual(1, actual.Count);
        Assert.AreEqual(expected.Name, actual.FirstOrDefault().Name);
    }
    In the above, I have used Future Mocking along with its feature - Future Constructor Mocking.

    I hope this gives a starting point for your test scenario. Please, do not hesitate to contact us again if there is anything else, we can help you with.

    Regards,
    Kaloyan
    Telerik
    Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
  3. DevCraft R3 2016 release webinar banner
  4. Nacho
    Nacho avatar
    15 posts
    Member since:
    Oct 2013

    Posted 17 Feb 2014 in reply to Kaloyan Link to this post

    Thanks for your reply Kaloyan.

    With the proposed solution how do differentiate different instances? Modifying your method slightly so it resembles the real one:

    public ICollection<DirectoryInfo> ReadCatalog(string familyName)
    {
        // Root folder for the family
        string familyFolder = this.GetFamilyFolder(familyName);
        DirectoryInfo familyFolderInfo = new DirectoryInfo(familyFolder);
     
        var collection = new List<DirectoryInfo>();
        foreach (DirectoryInfo subFamilyFolderInfo in familyFolderInfo.EnumerateDirectories())
        {
            subFamilyFolderInfo.EnumerateDirectories();
            foreach (DirectoryInfo childFamilyFolderInfo in familyFolderInfo.EnumerateDirectories())
            {
                collection.Add(childFamilyFolderInfo);
            }
        }
     
        return (ICollection<DirectoryInfo>)collection;
    }

    How could I test the two loops if I can only set one expectation?

    On the other hand just a proposal, if a method call makes no sense (like adding a return to a constructor arrange) could be possible to throw an exception to be aware that this is an invalid construct.

    Thanks again for your help!
  5. Answer
    Kaloyan
    Admin
    Kaloyan avatar
    872 posts

    Posted 19 Feb 2014 Link to this post

    Hi again Nacho,

    This is interesting scenario for which you can use the JustMocks Sequential Mocking feature. You can try the following test:
    [TestMethod]
    public void TestMethod1()
    {
        // Arrange
        // Defining some parameters for the test.
        var expected1 = new DirectoryInfo("test1");
        var expected2 = new DirectoryInfo("test2");
        var passedString = @"testRoot\DrivesData\TestFamily";
     
        // Instantiating our class under test.
        var sUT = new TestClass();
     
        // This will arrange the constructor of any new DirectoryInfo instance,
        //  that matches the passedString argument, to do nothing.
        Mock.Arrange(() => new DirectoryInfo(passedString)).DoNothing();
     
        // Dummy instance, needed for the future arrange on the next line.
        var directoryInfo = new DirectoryInfo("test");
     
        // Arranging directoryInfo.EnumerateDirectories() to return a fake collection
        //  no matter the instance that is called from. This is also called Future Mocking.
        //  Additionally I have applied sequential mocking in order to arrange to correct return
        //  value for the specific call iteration.
        Mock.Arrange(() => directoryInfo.EnumerateDirectories())
            .IgnoreInstance()
            .Returns(new DirectoryInfo[] { expected1 }).InSequence();
     
        Mock.Arrange(() => directoryInfo.EnumerateDirectories())
            .IgnoreInstance()
            .Returns(new DirectoryInfo[] { expected2 }).InSequence();
     
        // Act
        var actual = sUT.ReadCatalog(passedString);
     
        // Assert
        Assert.AreEqual(1, actual.Count);
        Assert.AreEqual(expected2.Name, actual.FirstOrDefault().Name);
    }

    About your proposal, I must say it is a thing we defenetely must consider for our future releases. So, thank you for the idea!

    Regards,
    Kaloyan
    Telerik
    Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
Back to Top