This is a migrated thread and some comments may be shown as answers.

mscorlib mocking without access to instance

5 Answers 104 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Kenneth
Top achievements
Rank 1
Kenneth asked on 20 Aug 2010, 09:02 PM
The example, in the documentation, for mocking mscorlib types assumes that you have direct access to the object you want to mock from the unit test. However, I'm trying to mock a filesystem scenario and the code that I'm testing creates its own instance of the DirectoryInfo type that I want to mock.

Here's what my code looks like:

public class TypeBeingTested
    {
        public TypeBeingTested()
        {
            var folder = new DirectoryInfo("C:\\Program Files");
            var folders = folder.GetDirectories("Microsoft Visual Studio 8");
 
            if (folders.Length == 0)
                throw new DirectoryNotFoundException("Visual Studio 2005 is not installed.");
        }
    }

I want to test the scenario where the Visual Studio folder cannot be found on my disk. The way I thought I could do this, looks like this:

[TestInitialize]
       public void Mock_DirectoryInfo()
       {
           Mock.Partial<DirectoryInfo>().For<string>((folder, name) => folder.GetDirectories(name));
       }
 
       [TestMethod]
       [ExpectedException(typeof(DirectoryNotFoundException))]
       public void Missing_Program_Files_folder_throws_exception()
       {
           var folder = new DirectoryInfo("C:\\Program Files");
 
           Mock.Arrange(() => folder.GetDirectories("Microsoft Visual Studio 8")).Returns(new DirectoryInfo[] {});
 
           var instance = new TypeBeingTested();
 
           Assert.Fail("No exception was raised in TypeBeingTested constructor.");
 
       }

Obviously, that instance of "folder" in my test method isn't being used for anything other than the Arrange statement... I knew as I was writing that, that this wasn't going to work; but, I can't figure out how this test should be constructed.

Can anyone help?

Thanks!

5 Answers, 1 is accepted

Sort by
0
Ricky
Telerik team
answered on 25 Aug 2010, 01:04 PM
Hello Kenneth,

Thanks for bringing up the issue. However, I wrote the test in a bit different way to better illustrate the problem:


var folder = new DirectoryInfo("C:\\Program Files");
bool called = false;
Mock.Arrange(() => folder.GetDirectories("Microsoft Visual Studio 8"))
    .DoInstead(() => called = true)
    .Returns(new DirectoryInfo[0]);
var instance = new TypeBeingTested();
Assert.IsTrue(called);
Assert.Fail("No exception was raised in TypeBeingTested constructor.");

In line Assert.IsTrue, the test is failing as the instance created inside TypeBeingTested is different from what is created in the test method. As, we can see that the setup is applied to the instance and to make things even more clear, Let's consider the following example:

[TestMethod]
public void ShouldTreatSetupsForTwoDifferentInstancesDifferently()
{
    var foo1 = Mock.Create<Foo>();
    var foo2 = Mock.Create<Foo>();
    Mock.Arrange(() => foo2.Echo(Arg.IsAny<int>())).Returns(10);
    Mock.Arrange(() => foo1.Echo(Arg.IsAny<int>())).Returns((int arg) => arg);
    Assert.Equal(foo2.Echo(12), 10);
    Assert.Equal(foo1.Echo(12), 12);
}

Now, here we can see that different instances of the Foo class is meant to be evaluated differently. Therefore, the setup made on instance 1 should not intercept with the setup on instance 2.

This also a basic to the object oriented design where two different instances should never interfere to each other. Moreover, making instances flat, breaks the integrity as well.

Currently, the solution is that you have an overload in TypeBeingTested class that accepts a DirectoryInfo instance.

However, we have opened an item in our JustMock backlog to provide an additional modifer named "IgnoreInstance" for  Mock.Arrange(..) :


Mock.Arrange(() => folder.GetDirectories("Microsoft Visual Studio 8"))
    .DoInstead(() => called = true)
    .Returns(new DirectoryInfo[0]).IgnoreInstance();

This will evaluate the expected setup, no matter if the instance is newly created and has no relation with the one used inside  Mock.Arrange.

Hope this information is useful.

Regards,
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Kenneth
Top achievements
Rank 1
answered on 25 Aug 2010, 02:23 PM
Thank you for your great explanation. I can understand the difficulty in my scenario, and I can imagine cases where an "IgnoreInstance" setting might be helpful, I don't think this is really one of them. With an object like DirectoryInfo, I can very definitively identify specific instances without having a direct reference to it. Much like web page caching is often based on the parameters into the page, I think being able to identify the instance based on the values passed to the constructor, would be intuitive and quite powerful.

Perhaps this wouldn't work for all types, but it would work for many (if not most) and the exceptions would be easy to explain to a developer. Obviously, if I don't use a constructor to initialize the object, I wouldn't be able to "remotely" identify a specific instance, but then I'd be in the same situation I'm in now, where I might want to use an "IgnoreInstance" setting or refactor my tested code to accept a dependency injection. (Unfortunately, in my current situation, that is not an option for more than just technical reasons; I respect a product that encourages good design, but don't like products that require it!)

Thank you, once again, for your explanation... so far, I'm really enjoying JustMock, and have included it in my recommended purchases to my client.

Ken
0
Ricky
Telerik team
answered on 27 Aug 2010, 12:24 PM
Hi Kenneth,

Thanks again for your explanation and it's great that you have included JustMock in your recommended purchase list.

Regarding executing mock instance for similar arguments and constructor type rather using IgnoreInstance is also a great solution to this problem. Only, there will be few exceptions, when you are mocking members from interfaces or mocking virtual members that are intercepted via inheritance. However, I am adding this feature to the backlog for running mock instance for similar arguments in same test context (in this case test method) for sealed classes and final methods.

Hope, it will solve the problem you're having.

Kind Regards,
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Kenneth
Top achievements
Rank 1
answered on 27 Aug 2010, 02:41 PM
Thank you... your reputation for customer service (as you've demonstrated by quickly and competently engaging with us in these forums) is a primary reason why I recommended JustMock over Typemock Isolator.

Do you have a public view into your product backlog for JustMock, like I see you have for some of your other products? I'd love to track the progress of some of the ideas that come up in these forums.

Thanks!
0
Chris
Telerik team
answered on 27 Aug 2010, 03:52 PM
Hello Kenneth,
Thanks for the nice words and thanks for bringing this question up.
We've already planned to include JustMock in the Public Issue Tracking System and this will happen in the next 2-3 weeks. We just want to gather some more feedback till then.

Sincerely yours,
Chris
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
Tags
General Discussions
Asked by
Kenneth
Top achievements
Rank 1
Answers by
Ricky
Telerik team
Kenneth
Top achievements
Rank 1
Chris
Telerik team
Share this question
or