desautelsj
desautelsj

Reputation: 3725

How to setup property when mocking a concrete class

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

Answers (4)

Daniel
Daniel

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

Stephen Byrne
Stephen Byrne

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.

  • If you are trying to test the 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)
  • If you are passing the 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

AggieEric
AggieEric

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

ravibhagw
ravibhagw

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

Related Questions