In this entry we start with what was discussed in the previous entry regarding the unit tests.
A mock is an object which attempts to supplant a dependency of a class. The idea is that a mock object allows us to create an object of which we have total control, in this way, we can define what the return value of its functions will be. From the point of view of the unit tests, the mocks are important because they help us to focus our tests in a specific class without having the hassle of dealing with its dependencies. This is especially important when working with external dependencies of our software, such as a database, computer text files, and even web services.
Our mission then is to use a mock object to build the dependency that has the TransfersService class, in this way, we can concentrate on testing that class, and not its dependencies.
We will help the Moq library to do this. Install the Moq Nuget package (I’m going to use version 4.9).
Basically what we will do is use Moq to build an object that implements ITransferValidationService and that its function DoValidations returns the result that suits us according to the test we do. Now, our automatic tests that now use Moq:
[TestMethod] public void TransferBetweenAccountsWithInsufficientFundsThrowsAnException() { // Prepare Exception expectedException = null; Account origin = new Account() { Funds = 0 }; Account destination = new Account() { Funds = 0 }; decimal amount = 5m; var mock = new Mock<ITransferValidationService>(); string errorMessage = "error message"; mock.Setup(x => x.DoValidations(origin, destination, amount)).Returns(errorMessage ); var service = new TransfersService(mock.Object); // Test try { service.TransferBetweenAccounts(origin, destination, amount); Assert.Fail("An exception should have been thrown"); } catch(Exception ex) { expectedException = ex; } // Verify Assert.IsTrue(expectedException is ApplicationException); Assert.AreEqual(errorMessage, expectedException.Message); } [TestMethod] public void TransferBetweenAccountsEditTheFunds() { // Prepare Account origen = new Account() { Funds = 10 }; Account destino = new Account() { Funds = 5 }; decimal amount = 7m; var mock = new Mock<ITransferValidationService>(); mock.Setup(x => x.DoValidations(origin, destination, amount)).Returns(string.Empty); var service = new TransfersService(mock.Object); // Test service.TransferirEntreCuentas(origin, destination, amount); // Verify Assert.AreEqual(3, origin.Funds); Assert.AreEqual(12, destination.Funds); } }
As we can see, in both methods we declare an object of name mock, which will supplant the dependence of the class we want to test. Then we use the Setup method to indicate that, when the function receives the indicated parameters, it must return what we place in the Returns function.
In this way we have completely isolated the functionality of our class, and so we can concentrate on testing it, without having to give importance to its dependencies. This is crucial because, if some of our automatic tests fail, we can be sure that it is because the problem is in the class we are testing, and not in that a dependency has stopped working.
I want to emphasize, again, that to make a unit test it is not obligatory to make mocks of all the dependencies of our classes. Remember that a unit test is a test that verifies a unit of work, not necessarily a method of a class. As a general rule, one tries to mock dependencies of databases, web services and other external resources. However, for the case of classes, one sometimes does not, because it is understood that the relationship between classes is part of the unit of work we want to test. In the end it is a matter of context, and you should do what is most productive in your judgment.
Rarely a software is made up of a single method or class, and it is seldom that our software does not work with external components. Given this, it is also important to make tests that verify the harmony between our software and external components. Let’s see the integration tests in the next post.
Summary
The mocks help us to control the behavior of the dependencies of our classes. This is useful when we perform unit tests, because it allows us to determine the behavior of the dependencies of the class we want to test, in order to concentrate on the class we want to test.
Other posts of this series:
- Basic Concepts of Automatic Testing
- Fundamentals of Unit Tests
- Fundamentals of Mocks (Unit Tests) (current post)