Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should I use the .As method of Moq?

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?

like image 243
user11081980 Avatar asked Dec 10 '22 22:12

user11081980


2 Answers

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());
like image 109
Daniel Oliveira Avatar answered Dec 24 '22 07:12

Daniel Oliveira


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);
}
like image 21
PiotrWolkowski Avatar answered Dec 24 '22 07:12

PiotrWolkowski