Are you looking for a solution to speed up your unit test development? Or perhaps you want to write more stable and reliable unit tests? Or you simply want the unit tests to be isolated from their environment? Then you need JustMock.
The following items will be covered to help you write better tests:
Mocking is a process used in unit testing where the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not in handling the behavior or state of external dependencies like database or service that could also break your test. In mocking, the dependencies are replaced by closely controlled replacement objects, such as a mock object, that simulate the behavior of the real ones.
The mock object is not simply an object with hard coded values to return. The mock object also acts as a collaborator and is subject to dynamic behavioral modification and verification. Such a collaborator will help you achieve your desired unit-testing goal for a given system and a controlled set of inputs. This is achieved by having the mock object stand-in for the real object and return data or execute a behavior you have specified.
To get a complete overview of mocks, an informative article named “Mocks aren’t stubs” by Martin Fowler is a must read.
http://martinfowler.com/articles/mocksArentStubs.html
After you are finished, it’s time to give you a good grasp of mocking with a real world example.
I am going to implement part of a banking system. This includes the transfer of funds between two different currency accounts using the currency converter service.
To begin, I have created an ICurrencyService interface:
public
interface
ICurrencyService
{
decimal
GetConversionRate(
string
fromCurrency,
string
toCurrency);
}
This service interface will be injected to an AccountsService class that is responsible for the transfer funds operation. Once the source and destination account objects are passed to the operation, it will then withdraw from source, convert the currency based on current rate, and finally deposit the converted amount to the target account. There will be a check for available balance, authorization, approval, etc. in more complex scenarios, but they are out of scope for this article.
public
class
AccountService : IAccountService
{
private
readonly
ICurrencyService currencyService;
public
AccountService(ICurrencyService currencyService)
{
this
.currencyService = currencyService;
}
public
void
TransferFunds(Account from, Account to,
decimal
amount)
{
from.Withdraw(amount);
decimal
conversionRate = currencyService.GetConversionRate(from.Currency, to.Currency);
decimal
convertedAmount = amount * conversionRate;
to.Deposit(convertedAmount);
}
}
The goal is to validate or assert the transfer funds operation between two accounts. Therefore, this is the system under test (SUT), and the currency service is the collaborator which will be mocked. I am using JustMock for this purpose, but the core concept is the same with other mocking tools.
First I will create the mock of the ICurrenyService interface:
ICurrencyService currencyService = Mock.Create<ICurrencyService>();
From the account service implementation, I can see that GetCurrencyService is being called to retrieve the conversation rate. The next step is to set an expected return value for it.
Mock.Arrange(() => currencyService.GetConversionRate(
"GBP"
,
"CAD"
))
.Returns(2.20m).MustBeCalled();
Mock.Arrange is the entry-point for setting expected behavior for a given mock. This line is self-explanatory, but I did use one extra option: MustBeCalled. This ensures that if currencyService.GetConversationRate is not called with the above criteria, then it will fail the test.
Since I have finished setting the behavior, I will then create an instance of the AccountService class followed by the source and destination Account classes.
var accountService =
new
AccountService(currencyService);
var canadianAccount =
new
Account(0,
"CAD"
);
var britishAccount =
new
Account(0,
"GBP"
);
Next, I will add some money into the GBP account:
britishAccount.Deposit(100);
Then transfer it to my Canadian account:
accountService.TransferFunds(britishAccount, canadianAccount, 100);
Once the transfer is complete, I need to make sure the operations happened as expected. I will first assert the balance of the two accounts:
Assert.AreEqual(0, britishAccount.Balance);
Assert.AreEqual(220, canadianAccount.Balance);
Then I will assert the mock to verify whether the required GetConversationRate method is called as expected:
Mock.Assert(currencyService);
And here is what the whole test looks like:
[TestMethod]
public
void
TestTransferFunds()
{
// Arrange
ICurrencyService currencyService = Mock.Create<ICurrencyService>();
Mock.Arrange(() => currencyService.GetConversionRate(
"GBP"
,
"CAD"
))
.Returns(2.20m).MustBeCalled();
var accountService =
new
AccountService(currencyService);
var canadianAccount =
new
Account(0,
"CAD"
);
var britishAccount =
new
Account(0,
"GBP"
);
// Act
britishAccount.Deposit(100);
accountService.TransferFunds(britishAccount, canadianAccount, 100);
// Assert
Assert.AreEqual(0, britishAccount.Balance);
Assert.AreEqual(220, canadianAccount.Balance);
Mock.Assert(currencyService);
}
You may have noticed that there were three easy steps involved with the unit test. This is part of a well-known pattern called Arrange – Act – Assert or AAA. JustMock’s syntax is implemented to strictly follow the AAA pattern to give you a well-structured flow, even in the most complex scenarios.
In this post, I described how mocking will help you. I then built an example project and test to illustrate how to proceed. Mocking helps you write clean unit tests, and it enables you to focus on the test logic by isolating external factors. With mocking, unit testing is no longer a chore.
This post was originally published in 2012 by Mehfuz Hossain and has been revised for accuracy and completeness.
Mihail Vladov is a Software Engineering Manager at Progress. He has more than a decade of experience with software and product development and is passionate about good software design and quality code. Mihail helped develop the WPF controls suite and Document Processing libraries which are used by thousands of developers. Currently, he is leading the JustMock team. In his free time, he loves to travel and taste different foods. You can find Mihail on LinkedIn.