Hi I am new to Moq framework and have some questions about how to use it. I will give an example and hope for answers.
I have two classes, an interface and and an implementation:
public class Vehicle{ public string RegistrationNumber {get; set;} public long VehicleIdentifier { get; set; } public Tyre TyreSpecification { get; set; } } public class Tyre { public long NumberOfTyres {get; set;} public long TyreSize { get; set;} } public interface ISelecter { Vehicle GetVehicleByRegistrationNumber(string registrationNumber); Tyre GetTyreSpecification(long vehicleIdentifier); } public class Selecter : ISelecter { public Vehicle GetVehicleByRegistrationNumber(string registrationNumber) { var vehicle = 'Database will give us the vehicle specification'; //Then we do things with the vehicle object //Get the tyre specification vehicle.TyreSpecification = GetTyreSpecification(vehicle.VehicleIdentifier); return vehicle; } public Tyre GetTyreSpecification(long vehicleIdentifier) { var tyre = 'external manufacture system gets the tyre specification'; //Then do thing with the tyre before returning the object return tyre; } }
I want to write two tests for those methods. The problem is when I write the test for GetVehicleByRegistrationNumber
I do not know how to mock the method call to GetTyreSpecification
.
The test methods look like this:
[TestClass] public class SelecterTest { [TestMethod] public void GetTyreSpecification_test() { //Arrange var tyre = new Tyre { NumberOfTyres = 4, TyreSize = 18 }; var mockSelecter = new Mock<ISelecter>(); mockSelecter.SetUp(s=>s.GetTyreSpecification(It.IsAny<long>())).Returns(tyre); //Act var tyreSpec = mockSelecter.Object.GetTyreSpecification(123456); //Assert Assert.IsTrue(tyreSpec.NumberOfTyres == 4 && tyreSpec.TyreSize == 18); } [TestMethod] public void GetVehicleByRegistrationNumber_test() { //Arrange var vehicle= new Vehicle { VehicleIdentifier = 123456, RegistrationNumber = ABC123, TyreSpecification = new Tyre { Tyresize = 18, NumberOfTyres = 4 }}; var mockSelecter = new Mock<ISelecter>(); mockSelecter.SetUp(s=>s.GetVehicleByRegistrationNumber(It.IsAny<string> ())).Returns(vehicle); //Act var vehicle = mockSelecter.Object.GetVehicleByregistrationNumber(123456); //Assert Assert.IsTrue(vehicle.Registrationnumber == "ABC123"; } }
In the test method GetVehicleByRegistrationNumber_test
how do I mock the call to getTyreSpecification
?
We can mock runInGround(String location) method inside the PersonTest class as shown below. Instead of using mock(class) here we need to use Mockito. spy() to mock the same class we are testing. Then we can mock the method we want as follows.
Trying to mock a method that is called within another method. // code part public virtual bool hello(string name, int age) { string lastName = GetLastName(); } public virtual string GetLastName() { return "xxx"; } // unit test part Mock<Program> p = new Mock<Program>(); p. Setup(x => x. GetLastName()).
The Moq framework is an open source unit testing framework that works very well with . NET code and Phil shows us how to use it.
Unit testing is a powerful way to ensure that your code works as intended. It's a great way to combat the common “works on my machine” problem. Using Moq, you can mock out dependencies and make sure that you are testing the code in isolation.
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.
You shouldn't be trying to mock a method on the class you're trying to test. Mocking frameworks are used to replace the actual calls made to dependencies that your class takes in with fake calls so that you can focus on testing the behaviour of your class without being distracted by external dependencies that it has.
There are no external dependencies taken in by your Selecter
class so you don't need to mock anything. I would always advocate not mocking if you don't have to and testing the actual code itself. Obviously, to keep your test atomic, you would need to mock calls to external dependencies if there were any.
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