Reputation: 3725
I am attempting to mock (using Moq) a classes and interfaces that are defined in a third party SDK. Here's a simplified example of what they look like:
public interface IVehicle
{
string Type { get; }
}
public class Vehicle
{
public string Type { get; }
}
public class Jeep : Vehicle, IVehicle
{
}
I can easily mock the interface like so:
var mockVehicle = new Mock<IVehicule>(MockBehavior.Strict);
mockVehicle
.SetupGet(v => v.Type)
.Returns("My custom vehicle");
but I need to write a unit test specific to the Jeep
class and I can't figure out how to mock it.
My first attempt:
var mockJeep = new Mock<Jeep>(MockBehavior.Strict);
yielded the following error:
Moq.MockException: IVehicle.Type invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
which is understandable since I haven't setup the Type
property.
My second attempt:
var mockJeep = new Mock<Jeep>(MockBehavior.Strict);
mockJeep
.SetupGet(v => v.Type)
.Returns("Jeep");
yielded the following error message:
System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: v => v.Type
which is also understandable since the Type
property on the Vehicle
class is not virtual.
So my question is: is there a way to setup the Type
property when mocking the Jeep
class?
Upvotes: 1
Views: 3486
Reputation: 174
disclaimer: I work at Typemock.
By using Typemock Isolator you will be able to mock non-virtual methods and won't need to change your code in order to do so, in this specific example you can fake an instance of Jeep
and then modify his methods behavior.
Here is an example for a test that mock Jeep
:
[TestMethod]
public void CallFakeJeepType_WillReturnWantedString()
{
var jeep = Isolate.Fake.Instance<Jeep>();
Isolate.WhenCalled(() => jeep.Type).WillReturn("fake jeep");
var result = Jeep.DoSomthing(jeep);
Assert.AreEqual("fake jeep", result);
}
note: if the Jeep.Type
had a setter as well you could use typemock's True property and the test will look like this
var jeep = Isolate.Fake.Instance<Jeep>();
jeep.Type = "fake jeep";
Upvotes: 0
Reputation: 7505
So my question is: is there a way to setup the Type property when mocking the Jeep class?
The short answer is no, you cannot Mock a non virtual member using Moq.
However, I don't think there's any reason in this case to mock anything, since there isn't any behaviour to mock. The only member you have there is a simple string
property. So even if you could, you probably shouldn't.
Jeep
class, just test it directly
without mocking any of its members; mocking should only be used when you need to control the behaviour of one of the dependencies of the System Under Test (SUT)Jeep
class to another method as a dependency in order to test that method, you can just
new
up a Jeep
instance and pass it in your test in this case; since it doesn't
have any boundary-crossing behaviours (network calls, etc) then there
is no need to mock it.You might also consider that if it's the second case, you ought to be able to pass an IVehicle
instead of a Jeep
, in which case mocking is back on the table (although as mentioned, there is no apparent need in this case)
Incidentally, your hierarchy looks a little off - why does Vehicle
not itself implement IVehicle
?
i.e.
public interface IVehicle
{
string Type { get; }
}
public class Vehicle: IVehicle
{
public string Type { get; }
}
public class Jeep : Vehicle
{
}
Then Jeep
will already be both a Vehicle and an IVehicle
:)
Upvotes: 0
Reputation: 1209
Since this is a rather generalized question, I'm going to suggest a different approach. Mocking / Faking / Stubbing a class or interface that you don't control is generally difficult. Furthermore, this typically leads to replicating someone else's functionality in your mock instance.
A better approach is to isolate interaction with the external library into a wrapping class that you control the interface and implementation of. This will allow you to easily mock/fake/stub your interface for testing consumers of your interface.
This still leaves the question of what to do with the isolation layer class. For this, you'll need to do more of an integration test that actually exercises both your wrapping class and the external class. Then, verify that you get the expected behavior from the combination of your class and the external classes you're wrapping.
Upvotes: 1
Reputation: 1740
The best Moq can do in this case is create a proxy as a derived class of Jeep
but it cannot override the non-virtual Type
property. Remember that when you attempt to create a Mock with Moq, the framework generates a class that either implements the target interface or derives from the target class.
Microsoft Fakes work well with Moq provided that you have the Visual Studio license level to access it.
Upvotes: 2