0

I am building a simple Airport program where planes can only take off / land if the weather is sunny and not stormy. This depends on the Weather class (which randomises the weather between sunny and stormy). However, for my tests, I want to mock the weather so I can test for all cases.

Here is my Weather.cs:

using System;

namespace ClassNameWeather
{
    public class Weather
    {
        public Weather()
        {

        }

        public string Forecast()
        {
            Random random = new Random();
            var weather = random.Next(1, 11);
            if (weather == 1 || weather == 2)
            {
                return "stormy";
            }
            else
            {
                return "sunny";
            }
        }
    }
}

Here is my Airport.cs:

using System;
using System.Collections.Generic;
using ClassNamePlane;
using ClassNameWeather;

namespace ClassNameAirport
{
    public class Airport
    {
        private string _AirportName { get; set; }
        public List<Plane> planes;
        private Weather _weather = new Weather();

        public Airport(string _airportName, Weather weather)
        {
            planes = new List<Plane>();
            _AirportName = _airportName;
        }

        public void Land(Plane plane)
        {
            if (_weather.Forecast() != "stormy")
            {
                planes.Add(plane);
                Console.WriteLine($"{ plane.Name } has landed at {_AirportName}");
            }
            else
            {
                throw new Exception("It's too stormy to land");
            }
        }

        public void TakeOff(Plane plane)
        {
            if (_weather.Forecast() != "stormy")
            {
                planes.Remove(plane);
                Console.WriteLine($"{ plane.Name } has departed from {_AirportName}");
            }
            else
            {
                throw new Exception("It's too stormy to take off");
            }
        }

        public int GetPlaneCount()
        {
            Console.WriteLine($"Number of planes at {_AirportName}: {planes.Count}");
            return planes.Count;
        }

        public void GetPlaneNames()
        {
            planes.ForEach(plane => Console.WriteLine((plane as Plane).Name));
        }

        public List<Plane> GetPlaneList()
        {
            return planes;
        }
    }
}

And here is the test that I'm trying to use the mock in:

using NUnit.Framework;
using ClassNameAirport;
using ClassNamePlane;
using ClassNameWeather;
using Moq;

namespace AirportTest
{
    public class AirportTest
    {
        Airport airport = new Airport("TestAirport", weather);
        Plane plane = new Plane("TestPlane");

        [Test]
        public void PlaneCanLand()
        {
            var weather = new Mock<Weather>();
            weather.Setup(x => x.Forecast()).Returns("sunny");
            airport.Land(plane);
            Assert.IsTrue(airport.planes.Contains(plane));
        }

        public void PlaneCanTakeOff()
        {
            airport.Land(plane);
            airport.TakeOff(plane);
            Assert.IsFalse(airport.planes.Contains(plane));
        }
    }
}

This line: Airport airport = new Airport("TestAirport", weather); is not working, saying the name weather does not exist.

Can anyone help me to make sure I am using Moq correctly? I'm new to C# and any advice is much appreciated.

Thank you!

UPDATE I have fixed this, but now receive the following error:

System.NotSupportedException : Unsupported expression: x => x.Forecast()
Non-overridable members (here: Weather.Forecast) may not be used in setup / verification expressions.

Does anyone know how to fix this please?

2
  • You've initialized the Weather inside Airport class like private Weather _weather = new Weather(); weather constructor parameter isn't used, you should rewrite your class, because the mock will not work Commented Aug 2, 2019 at 7:47
  • Thank you @PavelAnikhouski Do you mind explaining what you mean by rewrite the class? Commented Aug 2, 2019 at 7:55

2 Answers 2

2

You can introduce interface IWeather like

public interface IWeather
{
     string Forecast();
}

Than implement it in Weather class. Pass IWeather reference to AirPort class and setup a mock for that.

var weather = new Mock<IWeather>();
weather.Setup(x => x.Forecast()).Returns("sunny");
...
var airport = new Airport("TestAirport", weather.Object)

And do not initialize it in Airport class directly private Weather _weather = new Weather(); (your constructor argument is not used), do like this

public class Airport
{
    private string _AirportName { get; set; }
    public List<Plane> planes;
    private readonly IWeather _weather; 

    public Airport(string _airportName, IWeather weather)
    {
        planes = new List<Plane>();
        _weather = weather;
    }
...
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks @PavelAnikhouski this is super helpful! :) This seems to work, the only trouble I'm having is declaring a new instance of my Airport object in Main now that it's an interface. How would I create a new Airport object?
@jordantomiko You can initialize interface type variable using instance of class implementing this interface. IWeather weather = new Weather(); or var weather = new Weather(); The same for airport IAirport airport= new Airport(); or var airport= new Airport();
Thank you! This is what I thought, but I receive the error: Cannot implicitly convert type 'ClassNameWeather.Weather' to 'ClassNameWeather.IWeather'. An explicit conversion exists (are you missing a cast?) (CS0266) (Airport) when I try to initialize a new instance of my Airport in Main
@jordantomiko You should use interface type reference IWeather in Airport class, not the Weather class, like I posted in answer. And initialize var airport = new Airport("TestAirport", new Weather());
Thanks @PavelAnikhouski - I did all of this as you said in your answer but I still get that error. Perhaps it is the way I have introduced the weather interface? Does it need to be in a specific location in Weather.cs?
0

You have not declared the variable weather. I suggest that you create an Initialize method and attribute it with TestInitialze

[TestInitialize]
public void TestInitialize() 
{
    var weather = new Mock<Weather>();
    var airport = new Airport("TestAirport", weather)
}

3 Comments

Thank you - I tried this but it didn't work. At first it didn't like TestInitialize, so then I tried NUnit's [TestFixtureSetUp] instead but it still was not happy with the weather argument :(
What does the error say? You clearly havent declared the weather variable here: Airport airport = new Airport("TestAirport", weather); Plane plane = new Plane("TestPlane");
Don't worry, I have got it to work (it was to do with where I put it in my program) - thank you for your help. I am now getting the following error: System.NotSupportedException : Unsupported expression: x => x.Forecast() Non-overridable members (here: Weather.Forecast) may not be used in setup / verification expressions. but I will update my original question

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.