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?
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;
}
...
}
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)
}
Related
I'm using a unity container and I'm trying to resolve by passing the object to the parameterized constructor, I noticed the same constructor is called twice, the first time it takes appropriate values, and not sure why it is calling again and it overrides with a blank object, can someone help me what is happening over here, not able to solve it.
//////////////////////////////////////////////////////////////////////
if (container == null)
{
container = new UnityContainer().AddExtension(new Diagnostic());
container.RegisterType<ISubscribeService,OOrderProc.Common.SubscribeService.SubscribeService>();
container.RegisterType<IBaseOrderProcessing, BaseSubscribe>("Subscribe");
}
SubscribeDetails m = new SubscribeDetails();
m.SubscribeType = SubscribeType.ACTIVATE;
m.SubscribeName = "TEST";
var b = container.Resolve<IBaseOrderProcessing>("Subscribe",new DependencyOverride<BaseSubscribe>(new OOrderProc.Common.SubscribeService.SubscribeService(m)));
//////////////////////////////////////////////////////////////////////
public interface IBaseOrderProcessing
{
void ProcessOrder();
}
public interface ISubscribeService
{
SubscribeType SubscribeType { get; set; }
void ActivateSubscribe();
void UpgradeSubscribe();
}
// Strategy Pattern 1 => Subscribe is one of the "if" condition
public class BaseSubscribe : IBaseOrderProcessing
{
private ISubscribeService _SubscribeService = null;
public BaseSubscribe(ISubscribeService SubscribeService)
{
_SubscribeService = SubscribeService;
}
public void ProcessOrder()
{
if (_SubscribeService.SubscribeType == SubscribeType.ACTIVATE)
_SubscribeService.ActivateSubscription();
if (_SubscribeService.SubscribeType == SubscribeType.UPGRADE)
_SubscribeService.UpgradeSubscription();
}
}
// Writing another class to simplify is correct ?????
public class SubscribeService : ISubscribeService
{
private SubscribeDetails _Subscribedetails = null;
public SubscribeType SubscribeType { get; set; }
public SubscribeService(SubscribeDetails Subscribedetails)
{
_Subscribedetails = Subscribedetails;
SubscribeType = Subscribedetails.SubscribeType;
}
public void ActivateSubscription()
{
// Code to save the Subscribe details in the database
Console.WriteLine($"\n\nSubscribe {_Subscribedetails.SubscribeId} for {_Subscribedetails.SubscribeName} activated for order Id: {_Subscribedetails.OrderId}" +
$" from {_Subscribedetails.SubscribeStartDate} to {_Subscribedetails.SubscribeEndDate}");
}
public void UpgradeSubscription()
{
// Code to upgrade the Subscribe details in the database
Console.WriteLine($"\n\nSubscribe {_Subscribedetails.SubscribeId} for {_Subscribedetails.SubscribeName} upgraded for order Id: {_Subscribedetails.OrderId}" +
$" from {_Subscribedetails.SubscribeStartDate} to {_Subscribedetails.SubscribeEndDate}");
}
}
I resolved using below code:
container.RegisterType<IBaseOrderProcessing, BaseSubscribe>("Subscribe", new InjectionConstructor(new OOrderProc.Common.SubscribeService.SubscribeService((SubscribeDetails)obj)));
return container.Resolve<IBaseOrderProcessing>("Subscribe");
I am very new at Unity and I tried to integrate Huawei Mobile Service plugin and I got this error.
The type 'AndroidJavaObject' is defined in an assembly that is not referenced. You must add a reference to assembly 'UnityEngine.AndroidJNIModule
Is there anyone who encounter this problem before?
Thank you.
Edit
This code is belong to plugin.
using HuaweiMobileServices.Id;
using HuaweiMobileServices.Utils;
using System;
using UnityEngine;
namespace HmsPlugin
{
public class AccountManager : MonoBehaviour
{
public static AccountManager GetInstance(string name = "AccountManager") => GameObject.Find(name).GetComponent<AccountManager>();
private static HuaweiIdAuthService DefaultAuthService
{
get
{
Debug.Log("[HMS]: GET AUTH");
var authParams = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).SetIdToken().CreateParams();
Debug.Log("[HMS]: AUTHPARAMS AUTHSERVICE" + authParams);
var result = HuaweiIdAuthManager.GetService(authParams);
Debug.Log("[HMS]: RESULT AUTHSERVICE"+ result);
return result;
}
}
public AuthHuaweiId HuaweiId { get; private set; }
public Action<AuthHuaweiId> OnSignInSuccess { get; set; }
public Action<HMSException> OnSignInFailed { get; set; }
private HuaweiIdAuthService authService;
// Start is called before the first frame update
void Awake()
{
Debug.Log("[HMS]: AWAKE AUTHSERVICE");
authService = DefaultAuthService;
}
public void SignIn()
{
Debug.Log("[HMS]: Sign in " + authService);
authService.StartSignIn((authId) =>
{
HuaweiId = authId;
OnSignInSuccess?.Invoke(authId);
}, (error) =>
{
HuaweiId = null;
OnSignInFailed?.Invoke(error);
});
}
public void SignOut()
{
authService.SignOut();
HuaweiId = null;
}
}
}
Picture of the problem is here.
The problem is about my unity. I had no AndroidJNI module so I got this error. Finally I uninstall current version then install new version of Unity and problem is solved. In the new version AndroidJNI module is came automatically.
I'm new to .net and testing. My following code looks like this:
using System.Xml.Linq;
public class AnimalXmlService
{
public Animal GetAnimalInfoFromXml(string url) {
XElement xml_doc = GetXmlInfo(url);
if (xml_doc == null)
{
return null;
} else {
XElement animal_info = xml_doc.Element("Animal");
string animal_name = GetAnimalName(animal_info);
int animal_id = GetAnimalId(animal_info);
return new Animal(animal_id, animal_name);
}
}
private XElement GetXmlInfo(string url)
{
try
{
XElement animal_xml_info = XElement.Load(url);
return animal_xml_info;
}
catch
{
return null;
}
}
private int GetAnimalName(XElement animal_info)
{
....
}
}
My question is how do I stub the GetAnimalInfoFromXml to return a file? I have the sample xml file that I will be using instead of making a request. Here's my following test. I'm also wondering if there are better ways to refactor this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
namespace AnimalXmlService
{
[TestFixture]
public class AnimalXmlTest
{
[Test]
public void extracts_valid_name()
{
//get xml file?
animalService AnimalXmlService = new AnimalXmlService();
name = animalService.GetAnimalName(xml_file);
Assert.AreEqual(name, "bobby the tiger");
}
[Test]
public void extracts_valid_id()
{
//get xml file?
xml_file = fetch_file //is this appropriate?
animalService AnimalXmlService = new AnimalXmlService();
id = animalService.GetAnimalId(xml_file);
Assert.AreEqual(name, "2");
}
}
}
In this situations you can use test doubles.
First , you should make your codes more testable ( Breaking dependency )
public class AnimalXmlService
{
private readonly IXmlReader _xmlReader;
public AnimalXmlService(IXmlReader xmlReader)
{
this._xmlReader = xmlReader;
}
public Animal GetAnimalInfoFromXml(string url)
{
XElement xml_doc = _xmlReader.Load(url);
if (xml_doc == null)
{
return null;
}
else
{
XElement animal_info = xml_doc.Element("Animal");
string animal_name = GetAnimalName(animal_info);
int animal_id = GetAnimalId(animal_info);
return new Animal(animal_id, animal_name);
}
}
}
And then you should create a stub to replace your real dependency. ( Also you can use frameworks like NSubstitute,Mock,...)
public class XmlReaderStub : IXmlReader
{
public XElement XElement { get; set; }
public XElement Load(string url)
{
return XElement;
}
}
And finally
public class AnimalXmlTest
{
[Test]
public void extracts_valid_name()
{
var stub = new XmlReaderStub();
stub.XElement = new XElement(); // some XElement
animalService AnimalXmlService = new AnimalXmlService(stub);
name = animalService.GetAnimalName();
Assert.AreEqual(name, "bobby the tiger");
}
}
You can have another method in your class like the one below which returns an XmlDocument.
public XmlDocument GetXmlFile()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<Animal><Name>Bobby the tiger</Name></Animal>");
return doc;
}
hope your help :)
I search to make the result of the LinQ Variable bellow "ES" available in an other method.
public void Contract_ES(QCAlgorithm Algorithm_ES, Slice slice)
{
foreach(var chain in slice.FutureChains)
{
var ES = (from futuresContract in chain.Value.OrderBy(x => x.Expiry)
where futuresContract.Expiry > Algorithm_ES.Time.Date.AddDays(1)
select futuresContract).FirstOrDefault();
}
}
I downloaded QuantConnect just to get an idea what you're trying to do. The example below should at least not yield any errors, but I haven't tried the output.
using QuantConnect.Data;
using System.Linq;
using QuantConnect.Data.Market;
namespace QuantConnect.Algorithm
{
public interface IFuturesContractSelector_ES
{
FuturesContract GetFuturesContract_ES(QCAlgorithm Algorithm_ES, Slice slice);
}
public class Contract_ES : IFuturesContractSelector_ES
{
private readonly Slice _Slice;
private readonly QCAlgorithm _Algorithm_ES;
public Contract_ES(QCAlgorithm Algorithm_ES)
{
_Algorithm_ES = Algorithm_ES;
}
public Contract_ES(Slice slice)
{
_Slice = slice;
}
public FuturesContract GetFuturesContract_ES(QCAlgorithm Algorithm_ES, Slice slice)
{
foreach (var chain in slice.FutureChains)
{
if (chain.Value.Symbol.Value.StartsWith("ES"))
{
return (from futuresContract in chain.Value.OrderBy(x => x.Expiry)
where futuresContract.Expiry > Algorithm_ES.Time.Date.AddDays(1)
select futuresContract).FirstOrDefault();
}
}
return null;
}
}
}
Or you could do an extension on the Slice class:
using QuantConnect.Data;
using System.Linq;
using QuantConnect.Data.Market;
namespace QuantConnect.Algorithm
{
public static class SliceExtensions
{
public static FuturesContract GetFuturesContract_ES(this Slice slice, QCAlgorithm Algorithm_ES)
{
foreach (var chain in slice.FutureChains)
{
if (chain.Value.Symbol.Value.StartsWith("ES"))
{
return (from futuresContract in chain.Value.OrderBy(x => x.Expiry)
where futuresContract.Expiry > Algorithm_ES.Time.Date.AddDays(1)
select futuresContract).FirstOrDefault();
}
}
return null;
}
}
public class Test
{
public void TestMyMethod(Slice slice)
{
var contract = slice.GetFuturesContract_ES(new QCAlgorithm());
//... do something
}
}
}
I tried to create an interface then the method, result is :
using QuantConnect.Securities;
namespace Quant
{
public interface IFuturesContractSelector_ES
{
void GetFuturesContract_ES(QCAlgorithm Algorithm_ES, Slice slice);
}
}
public class Contract_ES : IFuturesContractSelector_ES
{
private readonly Slice _Slice;
private readonly QCAlgorithm _Algorithm_ES;
public Contract_ES(QCAlgorithm Algorithm_ES)
{
_Algorithm_ES = Algorithm_ES;
}
public Contract_ES(Slice slice)
{
_Slice = slice;
}
public void GetFuturesContract_ES(QCAlgorithm Algorithm_ES, Slice slice)
{
foreach(var chain in slice.FutureChains)
{
if (chain.Value.Symbol.StartsWith("ES"))
{
var ES = (from futuresContract in chain.Value.OrderBy(x => x.Expiry)
where futuresContract.Expiry > Algorithm_ES.Time.Date.AddDays(1)
select futuresContract).FirstOrDefault();
}
}
return ES;
}
}
At the Line return ES, I get this error :
The name "ES" does not exist in the current context.
Weird because I have another method build in this way, with no prob -_-
Maybe using foreach statement that cause the non possible return of "var ES" ?
Let's say I have a parameter in my ViewModel:
public string ChosenQualityParameter
{
get => DefectModel.SelectedQualDefectParameters?.Name ?? "Не выбран параметр";
}
and I have a class DefectModel with parameter SelectedQualDefectParameters.Name in it. I want to change the UI binded to ChosenQualityParameter, when theName parameter changes too.
But I don't know how to do this properly. Any suggestions? Thanks in advance.
You might define your ViewModel class like this:
public class ViewModel
{
private DefectModel _defectModel;
public ViewModel(DefectModel defectModel)
{
_defectModel = defectModel;
}
public string ChosenQualityParameter
{
get => _defectModel.SelectedQualDefectParameters?.Name ?? "Не выбран параметр";
}
}
I personally do not like such dependencies in viewmodels, but it might get the job done here. It seems to work in a console application anyway:
using System;
public class Parameters
{
public string Name { get; set; }
}
public class DefectModel
{
public Parameters SelectedQualDefectParameters { get; set; }
}
public class ViewModel
{
private DefectModel _defectModel;
public ViewModel(DefectModel defectModel)
{
_defectModel = defectModel;
}
public string ChosenQualityParameter
{
get => _defectModel.SelectedQualDefectParameters?.Name ?? "Не выбран параметр";
}
}
class Program
{
static void Main()
{
var defectModel = new DefectModel
{
SelectedQualDefectParameters = new Parameters
{
Name = "test"
}
};
var viewModel = new ViewModel(defectModel);
Console.WriteLine(viewModel.ChosenQualityParameter);
defectModel.SelectedQualDefectParameters.Name = "changed";
Console.WriteLine(viewModel.ChosenQualityParameter);
Console.ReadKey();
}
}
Thanks to #Knoop and #BartHofland, I've solved my issue by using INotifyPropertyChanged in my DefectModel and SelectedQualDefectParameters classes.
For setting ChosenQualityParameter I used MessagingCenter to send new value.