1

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?

Lawtonfogle
  • 974
  • 4
  • 17
  • 32
  • 1
    if something is `internal`, it probably shouldn't be tested. If it needs to be tested, it probably shouldn't be `internal`. see this for a related problem: http://stackoverflow.com/questions/25237971/how-to-write-unit-test-for-private-method-in-c-sharp-using-moq-framework – DLeh Oct 26 '15 at 20:32
  • 1
    That might be true of `private` scope, but `internal` is still visible at assembly level and could possibly need to be mocked for testing of other components in the assembly. – Bradford Dillon Oct 26 '15 at 20:42
  • 1
    @DLeh I don't agree with this in the following scenarios: 1) an internal/private method has side effects you need to bypass in a testing context and that functionality shouldn't be exposed publicly (like emailing or logging something), or 2) when multiple internal methods are called in succession and it becomes prohibitive to "predict" the state of the inputs and outputs of those methods. – moarboilerplate Oct 26 '15 at 20:43
  • @DLeh In the code that led to this question, the internal bit is being made visible to other assemblies. It probably should've been made public to begin with, but that was a choice made before my time and I'm trying to add unit tests without actually touching the code unless there is no other alternative. – Lawtonfogle Oct 26 '15 at 20:44
  • @Lawtonfogle if that's the case just add a new public property on the class that returns the value of the internal property and expose it on the interface – moarboilerplate Oct 26 '15 at 20:48
  • @Lawtonfogle Is the object you are passing the mock to expecting an `IMyObject` or a `MyObject`? – Bradford Dillon Oct 26 '15 at 20:52
  • Unfortunately, I think the problem you are going to run into is not so much that the property is internal, that can be gotten around, or that one exists on the interface and the other on the object, that too can be gotten around. The issue you are going to face is that the property isn't marked virtual thus you will get a `NotSupportedException` from Moq anytime you attempt to mock out its functionality. – Bradford Dillon Oct 26 '15 at 21:04
  • If you want to mock the class (`MyObject`), you need to make both the method and the property `virtual`, and you need to relax the accessibility of the property from `internal` to `protected internal`. – Jeppe Stig Nielsen Oct 26 '15 at 23:14

2 Answers2

1

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.

Bradford Dillon
  • 1,750
  • 13
  • 24
0

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.

moarboilerplate
  • 1,633
  • 9
  • 23