简体   繁体   中英

Moq a class that has an internal property and which implements an interface

So I have a class which both implements an interface and has an internal property. I need to mock both of these using Moq to test a method.

public interface IMyObject
{
    bool myMethod (String input);
}

public class MyObject : IMyObject
{
    public bool myMethod(String input) {...}
    internal bool myProperty {get;}
}

Now, I can mock either using Moq.

myMethod:

var myIObjectMock = mockRepository.Create<IMyObject>();
myIObjectMock.Setup(m => m.myMehtod(It.IsAny<String>())).Returns((String input) => {...});

myProperty:

var myObjectMock = mockRepository.Create<MyObject>();
myObjectMock.SetupGet(m => m.myProperty).Returns(...);

My question is, how do I mock both?

The core of the problem seems to be that I'm mocking an interface in the first case and a class in the second. So the solution I see is to make it so you can mock the property and the method by mocking either the class or the interface. This would mean either switching the internal property into a public property and put it as part of the interface or making the method virtual.

Is there a solution that does not involve changing the MyObject class?

I think the internal property not being on the interface is a bit of a warning, because it seems like you're waiting on some side effect of the class to set it which can lead to race conditions. Generally speaking, you should keep your properties immutable to avoid this kind of difficulty.

You mentioned the only 2 ways to work around this issue sanely. The least intrusive would be to make the method virtual. The other option, to add another field, isn't really that risky either. I would just make one of these changes and be done with it. The only other thing you could do would be to try to mix in an extension method on this class via the interface, which I would strongly recommend avoiding.

The first caveat is if your class definition and unit tests are in different assemblies, and they probably are, you will first have to add

[assembly: InternalsVisibleTo("<test project name>")]

to the AssemblyInfo.cs file of the assembly under test for the internal property to even show up to the unit test assembly.

As for needing two separate objects for Moq, you probably don't. Using your example from above

var mock = new Mock<MyObject>();
mock.As<IMyObject>().Setup(m => m.myMethod(It.IsAny<string>())).Returns(...);
mock.SetupGet(m => m.myProperty).Returns(...);

Unfortunately, that setup will throw a NotSupportedException because myProperty isn't virtual and Moq does not support mocking non-virtual members. You will have to look into a different mocking library for that type of functionality.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM