Ilyas
Ilyas

Reputation: 759

Moq - How to mock a property but allow method calls in C#?

I have the following code:

public class Weather
{
    public int Temperature { get; set; }
    public string Description { get; set; }
}

public class WeatherProvider
{
    public virtual Weather GetWeather()
    {
        var w = new Weather();
        var hour = Hour;
        if (hour < 5)
        {
            w.Description = "Sunny";
            w.Temperature = 12;
        }
        else if (hour < 12)
        {
            w.Description = "Windy";
            w.Temperature = 18;
        }
        else
        {
            w.Description = "Snow";
            w.Temperature = 2;
        }
        return w;
    }

    public virtual int Hour
    {
        get { return DateTime.Now.Hour; }
    }
}

var mockWeatherProvider = new Moq.Mock<WeatherProvider>(MockBehavior.Loose);
mockWeatherProvider.SetupGet(x => x.Hour).Returns(2);

var actualWeather = mockWeatherProvider.Object.GetWeather();//This line always returns null? WHY???
mockWeatherProvider.Verify(x=>x.GetWeather());

Can any one explain why the line var actualWeather = mockWeatherProvider.Object.GetWeather() returns null? I actually want the real method to be called. How can I achive this?

Upvotes: 2

Views: 2523

Answers (4)

MNGwinn
MNGwinn

Reputation: 2394

By using loose mocking, you're getting the default value for all your virtual properties and mthods. You want to use CallBase to tell Moq to fail back to the normal implementations.

Try this:

var mockWeatherProvider = new Moq.Mock<WeatherProvider>(MockBehavior.Loose) { CallBase = true };
mockWeatherProvider.SetupGet(x => x.Hour).Returns(2);

Upvotes: 3

J. Steen
J. Steen

Reputation: 15578

You haven't set up a behaviour for the GetWeather() method. Basically, what you're doing here

var mockWeatherProvider = new Moq.Mock<WeatherProvider>(MockBehavior.Loose);

is creating an empty object that looks like WeatherProvider. You have to tell the mockWeatherProvider what every method and property you want to use should do for you.


Edit, as I realised what you might actually want

If you're trying to mock what is essentially only half of your class, you're going about your implementation in the wrong way in this particular case. If you want to mock, say, what Hour returns you could provide that property to the WeatherProvider through some TimeProvider implementation or some such that the WeatherProvider depends on.

var mockedTimeProvider = new Moq.Mock<TimeProvider>(MockBehavior.Loose);
mockedTimeProvider.SetupGet(p => p.Hour).Returns(2);

var weatherProvider = new WeatherProvider(mockedTimeProvider.Object);
var actualWeather = weatherProvider.GetWeather();

Edit, again

Try setting your GetWeather() as not virtual, and then use stubbing instead of complete mocking to generate your mock. That should keep the method from being overridden with a default returner. Though, this way would mud up your design a bit, and not make things as clear as the above would. Separation of concern, and whatnot. =)

Upvotes: 6

Hinek
Hinek

Reputation: 9719

Try

var mockWeatherProvider = new Moq.Mock<WeatherProvider>(MockBehavior.Normal);

Upvotes: 0

Brian Dishaw
Brian Dishaw

Reputation: 5825

Your mock doesn't specify an implementation for the GetWeather call. Since you are operating on the mock object and not your concrete implementation, Moq will return the default value. In this case, the default value in null.

If you want to test the implementation, you should just use the concrete implementation and setup the behavior for Hour on that object. This will allow you to exercise the code you have implemented and verify the behavior.

Upvotes: 1

Related Questions