How to use Moq in unit test that calls another method in same class

The focus on mocking the class under test has blinded you to the actual problem.

From the comments in the class under test…

  • ‘Database will give us the vehicle specification’
  • ‘external manufacture system gets the tyre specification’

you actually expose two dependencies that should be injected into the class.

For the purpose of explaining this answer lets say those dependencies looked like this.

public interface IDatabase {
    Vehicle GetVehicleByRegistrationNumber(string registrationNumber);
}

public interface IExternalManufactureSystem {
    Tyre GetTyreSpecification(long vehicleIdentifier);
}

That would mean that the Selecter would need to be refactored to expect those dependencies.

public class Selecter : ISelecter {
    private IDatabase database;
    private IExternalManufactureSystem externalManufactureSystem;

    public Selecter(IDatabase database, IExternalManufactureSystem externalManufactureSystem) {
        this.database = database;
        this.externalManufactureSystem = externalManufactureSystem;
    }

    public Vehicle GetVehicleByRegistrationNumber(string registrationNumber) {
        //'Database will give us the vehicle specification'
        var vehicle = database.GetVehicleByRegistrationNumber(registrationNumber);

        //Then we do things with the vehicle object

        //Get the tyre specification
        vehicle.TyreSpecification = GetTyreSpecification(vehicle.VehicleIdentifier);

        return vehicle;
    }

    public Tyre GetTyreSpecification(long vehicleIdentifier) {
        //'external manufacture system gets the tyre specification'
        var tyre = externalManufactureSystem.GetTyreSpecification(vehicleIdentifier);

        //Then do thing with the tyre before returning the object

        return tyre;
    }
}

From there it would then be a matter of mocking only the dependencies explicitly needed to test the behavior of the method under test.

selecter.GetTyreSpecification has no need to access the database so there is no reason to mock and inject it for the test.

[TestMethod]
public void GetTyreSpecification_test() {
    //Arrange
    var vehicleIdentifier = 123456;
    var expected = new Tyre { NumberOfTyres = 4, TyreSize = 18 };

    var mockSystem = new Mock<IExternalManufactureSystem>();
    mockSystem.Setup(s => s.GetTyreSpecification(vehicleIdentifier)).Returns(expected);

    var selecter = new Selecter(null, mockSystem.Object);

    //Act
    var actual = selecter.GetTyreSpecification(vehicleIdentifier);

    //Assert
    Assert.AreEqual(expected, actual);
}

selecter.GetVehicleByRegistrationNumber however needs to be able to get the tyre specification from the other method so this test would need both dependencies mocked in order for it to be exercised to completion.

[TestMethod]
public void GetVehicleByRegistrationNumber_test() {
    //Arrange
    var vehicleIdentifier = 123456;
    var registrationNumber = "ABC123";
    var tyre = new Tyre { TyreSize = 18, NumberOfTyres = 4 };
    var expected = new Vehicle {
        VehicleIdentifier = vehicleIdentifier,
        RegistrationNumber = registrationNumber,
        TyreSpecification = tyre
    };

    var mockSystem = new Mock<IExternalManufactureSystem>();
    mockSystem.Setup(s => s.GetTyreSpecification(vehicleIdentifier)).Returns(tyre);

    var mockDatabase = new Mock<IDatabase>();
    mockDatabase.Setup(s => s.GetVehicleByRegistrationNumber(registrationNumber)).Returns(expected);

    var selecter = new Selecter(mockDatabase.Object, mockSystem.Object);

    //Act
    var actual = selecter.GetVehicleByRegistrationNumber(registrationNumber);

    //Assert
    Assert.IsTrue(actual.RegistrationNumber == registrationNumber);
}    

Now with that out of the way, if for example the Selecter class had the GetVehicleByRegistrationNumber as a virtual method,

public virtual Tyre GetTyreSpecification(long vehicleIdentifier) {
    //...code removed for brevity.
}

There is a way you can use moq to stub the subject under test and mock that method for testing. This is not always the best design and is considered a code smell. However there are situations where you will end up in this particular scenario.

[TestMethod]
public void GetVehicleByRegistrationNumber_test2() {
    //Arrange
    var vehicleIdentifier = 123456;
    var registrationNumber = "ABC123";
    var tyre = new Tyre { TyreSize = 18, NumberOfTyres = 4 };
    var expected = new Vehicle {
        VehicleIdentifier = vehicleIdentifier,
        RegistrationNumber = registrationNumber,
        TyreSpecification = tyre
    };        

    var mockDatabase = new Mock<IDatabase>();
    mockDatabase.Setup(s => s.GetVehicleByRegistrationNumber(registrationNumber)).Returns(expected);

    var selecter = new Mock<Selecter>(mockDatabase.Object, null) {
        CallBase = true //So that base methods that are not setup can be called.
    }

    selecter.Setup(s => s.GetTyreSpecification(vehicleIdentifier)).Returns(tyre);

    //Act
    var actual = selecter.Object.GetVehicleByRegistrationNumber(registrationNumber);

    //Assert
    Assert.IsTrue(actual.RegistrationNumber == registrationNumber);
} 

In the above example, when selecter.Object.GetVehicleByRegistrationNumber(registrationNumber) is called, the base Selecter wrapped by the mock will be called, which in turn will then call the mocked GetTyreSpecification that was overridden by the setup on the mocked subject under test.

You tend to see this when testing abstract classes with implemented members that have dependencies on abstract members.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)