When exactly do we need to use the .As
method provided by Moq?
From the Quickstart documentation:
// implementing multiple interfaces in mock
var foo = new Mock<IFoo>();
var disposableFoo = foo.As<IDisposable>();
// now the IFoo mock also implements IDisposable :)
disposableFoo.Setup(df => df.Dispose());
But I just don't get why we would want to do that. Could you give me a practical example?
You can use the As method when you need to test objects that implements multiple interfaces.
In this sample, the tested code have a specific behavior if the input object implements IDisposable also. Something like this:
public void Execute(IFoo input)
{
// do process...
IDisposable disposable = input as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
The class implementation:
public class ConcreteFoo: IFoo, IDisposable
{
...
}
EDIT:
The Moq's configuration needed:
var foo = new Mock<IFoo>();
var disposableFoo = foo.As<IDisposable>();
disposableFoo.Setup(df => df.Dispose());
// Verify whether the Dispose() method was called
// That's true only if we use the As method from Moq.
testedClass.Execute(disposableFoo);
disposableFoo.Verify(m => m.Dispose());
Okay, so an example. Let's say you have a transportation management software to manage movement of cars, flights, etc. There are different vehicles but they move on land or air (no sea to simplify the sample).
public interface IMovingOnLand
{
string Move();
}
public interface IMovingInAir
{
string Move();
}
And there is an express transport option for a vehicle/aircraft.
public interface IExpressTransport
{
string MoveDirectly();
}
There is a transport manager class which is responsible for moving all the vehicles/aircraft. And it handles express means of transportation bit differently than regular ones (for the sake of simplicity in this sample it only prints a different message depending whether it's IExpressTransport or not):
public class TransportManager
{
public string MoveItem(IMovingInAir airCraft)
{
if (airCraft is IExpressTransport)
{
return "Message from an express aircraft: " +
((IExpressTransport)airCraft).MoveDirectly();
}
return "Message from an aircraft: " + airCraft.Move();
}
public string MoveItem(IMovingOnLand landVehicle)
{
if (landVehicle is IExpressTransport)
{
return "Message from an express land vehicle: " +
landVehicle.Move() +
((IExpressTransport)landVehicle).MoveDirectly();
}
return "Message from a land vehicle: " + landVehicle.Move();
}
}
Now you would like to test if an airplane behaves differently than a car. And also, if a regular flight is handled differently than an express one. So you test your object as an IMovingInAir
object and as IExpressTransport
. To test only flight behaviour you can simply create it as Mock<IMovingInAir>
. But to extend a flight to an express one you have to use As<IExpressTransport>()
method:
[TestMethod]
public void TestTransportManager()
{
TransportManager manager = new TransportManager();
// Create a regular flight.
var flight = new Mock<IMovingInAir>();
flight.Setup(x => x.Move())
.Returns("Air craft moved to next stop.");
// Create a flight.
var flightExpress = new Mock<IMovingInAir>();
// Add standard behaviour.
flightExpress
.Setup(x => x.Move())
.Returns("Air craft moved to next stop.");
// Extend to express and add express flight behaviour.
flightExpress
.As<IExpressTransport>()
.Setup(x => x.MoveDirectly())
.Returns("Air craft moved directly to destination.");
// Get the results.
var res = manager.MoveItem(flight.Object);
var resExp = manager.MoveItem(flightExpress.Object);
// Sssert flight and express fligh returned different messages.
Assert.AreNotEqual(res, resExp);
// Assert the expected messages have been returned.
Assert.AreEqual("Message from an aircraft: Air craft moved to next stop.", res);
Assert.AreEqual("Message from an express aircraft: Air craft moved directly to destination.", resExp);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With