Introduction
Mock is a simple and lightweight isolation framework, which is built on the basis of anonymous methods and expression trees. To create them, it uses code generation, thus allowing to mock interfaces, virtual methods (and even protected methods) and does not allow to mock non-virtual and static
methods.
NOTE
In the market, there exist only two frameworks that allow to mock anything. These are TypeMockIsolator
and Microsoft Fakes, available in Visual Studio 2012 and higher. These frameworks, unlike Mock (which uses code generation) use CLR Profiling API, allowing to mock any method even for static
, virtual
and private
methods. IMHO, they are good for testing legacy code which is hard or impossible to refactor in one bunch.
In Mock, there is no separation between stubs and mocks. Or if to speak more formally, there is no separation for state verification and behavior verification. And despite the fact that it not always easy to draw the line between them, quite often the same element can be in both roles. We will consider examples from simple to complex. Initially, we will consider state verification and later, we will switch to behavior verification.
State Verification
As an example, we will consider set of unit tests for the following interface:
public interface ILoggerSomeDependency
{
string GetApplicationDirectory();
string GetDirectoryForDependencyByLoggerName(string loggerName);
string GetLoggerInstance{get;}
}
- Stub of method
GetApplicationDirectory
:
IloggerSomeDependency loggerDependency =
Mock.Of<ILoggerSomeDependency>(d=>d.GetApplicationDirectory()=="C:\\Windows\\Fenestra");
var currentDirectory = loggerDependency.GetApplicationDirectory();
Assert.That(currentDirectory,Is.EqualTo("C:\\Windows\\Fenestra"));
- Stub of method
GetDirectoryForDependencyByLoggerName
, always returns the same result:
ILoggerSomeDependency loggerDependency = Mock.Of<ILoggerSomeDependency>
(ld => ld.GetDirectoryForDependencyByLoggerName(It.IsAny<string>()) == "C:\\Merced");
string directory = loggerDependency.GetDirectoryForDependencyByLoggerName("anything");
Assert.That(directory, Is.EqualTo("C:\\Merced"));
- Stub of method
GetDirrectoryByLoggerName
, returns result depending on the argument:
Mock<ILoggerSomeDependency> stub = new Mock<ILoggerSomeDependency>();
stub.Setup(ld => ld.GetDirectoryForDependencyByLoggerName(It.IsAny<string>()))
.Returns<string>(name => "C:\\" + name);
string loggerName = "AnyLogger";
ILoggerSomeDependency logger = stub.Object;
string directory = logger.GetDirectoryForDependencyByLoggerName(loggerName);
Assert.That(directory, Is.EqualTo("C:\\" + loggerName));
- Stub of property
GetLoggerInstance
:
ILoggerSomeDependency logger = Mock.Of<ILoggerSomeDependency>(
d => d.GetLoggerInstance == "GetLoggerInstance");
string GetLoggerInstance = logger.GetLoggerInstance;
Assert.That(GetLoggerInstance, Is.EqualTo("GetLoggerInstance"));
- Setting behavior of few methods with one expression with the help of “mock functional specification” (was born in v4):
ILoggerSomeDependency logger =
Mock.Of<ILoggerSomeDependency>(
d => d.GetApplicationDirectory() == "C:\\Windows\\Fenestra" &&
d.GetLoggerInstance == "GetLoggerInstance" &&
d.GetDirectoryForDependencyByLoggerName
(It.IsAny<string>()) == "C:\\Windows\\Temp");
Assert.That(logger.GetApplicationDirectory(), Is.EqualTo("C:\\Windows\\Fenestra"));
Assert.That(logger.GetLoggerInstance, Is.EqualTo("GetLoggerInstance"));
Assert.That(logger.GetDirectoryForDependencyByLoggerName
("CustomLogger"), Is.EqualTo("C:\\Windows\\Temp"));
- Configuring of behavior of few methods with usage of method
Setup
(ancient or v3 syntax):
var stub = new Mock<ILoggerSomeDependency>();
stub.Setup(ld => ld.GetApplicationDirectory()).Returns("C:\\Windows\\Fenestra");
stub.Setup(ld => ld.GetDirectoryForDependencyByLoggerName(It.IsAny<string>())).Returns("C:\\Windows\\Temp");
stub.SetupGet(ld => ld.GetLoggerInstance).Returns("GetLoggerInstance");
ILoggerSomeDependency logger = stub.Object;
Assert.That(logger.GetApplicationDirectory(), Is.EqualTo("C:\\Windows\\Fenestra"));
Assert.That(logger.GetLoggerInstance, Is.EqualTo("GetLoggerInstance"));
Assert.That(logger.GetDirectoryForDependencyByLoggerName("CustomLogger"), Is.EqualTo("C:\\Windows\\Temp"));
Note
As it was already mentioned, Mock doesn't differ between mocks and stubs, but for us it will be easier to distinguish syntax of initialization of stubs. Mock functional specification syntax may be used for testing state condition (i.e. for stubs) and can't be used for configuring behavior. On the other hand, initialization of stubs with method Setup
can be more cumbersome and not always easy to grasp what we want to check behavior or state.
Behavior Verification
For testing behavior, we will use the following class and interface:
public interface ILogSaver
{
string GetLogger();
void SetLogger(string logger);
void Write(string message);
}
public class Logger
{
private readonly ILogSaver _logSaver;
public Logger(ILogSaver logWriter)
{
_logSaver = logWriter;
}
public void WriteLine(string message)
{
_logSaver.Write(message);
}
}
- Checking of calling of method
ILogSaver.Write
with object of class Logger
(with any argument):
var mock = new Mock<ILogSaver>();
var logger = new Logger(mock.Object);
logger.WriteLine("Greeting by logger!");
mock.Verify(lw => lw.Write(It.IsAny<string>()));
- Checking of calling method
ILogSaver.Write
with configured arguments:
var mock = new Mock<ILogSaver>();
mock.Verify(lw => lw.Write("Greeting by logger!"));
- Checking that method
ILogSaver.Write
was called only once (not more or less):
var mock = new Mock<ILogSaver>();
mock.Verify(lw => lw.Write(It.IsAny<string>()), Times.Once());
Note
There are many options of checking how many times some dependency was called. For this purpose, you can use the following methods of class Times
: AtLeast(int)
, AtMost(int)
, Exactly
, Between
and others.
- Checking behavior with the help of method
Verify
(you can use other convenient, for cases if you need to check few assumptions):
var mock = new Mock<ILogSaver>();
mock.Setup(lw => lw.Write(It.IsAny<string>()));
var logger = new Logger(mock.Object);
logger.WriteLine("Greeting by logger!");
mock.Verify();
- Checking of few callings with the help of method
Verify()
.
In some situations, it is convenient to use few methods of Verify
for checking of some callings. Instead of this, you can use mock-object and configure expected behavior with the help of Setup and check those assumptions with calling of one method Verify()
. This technique can be convenient for repeating of testing of Mock objects, created in configurations of method Setup
test.
var mock = new Mock<ILogSaver>();
mock.Setup(lw => lw.Write(It.IsAny<string>()));
mock.Setup(lw => lw.SetLogger(It.IsAny<string>()));
var logger = new Logger(mock.Object);
logger.WriteLine("Greeting by logger!");
mock.Verify();
Lyric Notes or Strict vs Loose Model
Mock supports two models of checking behavior: strict and loose. By default, loose model is used, which means that Class Under Test or CUT during execution in section Act can call any methods of dependencies and we are not obliged to point them all. As in the previous example, method logger.WriteLine
calls two methods of interface ILogSaver
: method Write
and SetLogger
. In case of usage MockBehavior.Strict
method Verify
will fail, if we will not point clearly, which methods of dependencies will be called:
var mock = new Mock<ILogSaver>(MockBehavior.Strict);
mock.Setup(lw => lw.Write(It.IsAny<string>()));
mock.Setup(lw => lw.SetLogger(It.IsAny<string>()));
var logger = new Logger(mock.Object);
logger.WriteLine("Greeting by logger!");
mock.Verify();
Usage of MockRepository
Class MockRepository
provides one more syntax for creating stubs and what is more important allows to preserve few mock objects and check complex behavior with calling of one method.
- Usage
MockRepository.Of
for creating stubs
Syntax is analogical to usage Mock.Of
, but allows to set behavior of different methods not via operator && but with usage of few methods where:
var repository = new MockRepository(MockBehavior.Default);
ILoggerSomeDependency logger = repository.Of<ILoggerSomeDependency>()
.Where(ld => ld.GetLoggerInstance == "GetLoggerInstance")
.Where(ld => ld.GetApplicationDirectory() == "C:\\Windows\\Fenestra")
.Where(ld => ld.GetDirectoryForDependencyByLoggerName
(It.IsAny<string>()) == "C:\\Windows\\Temp")
.First();
Assert.That(logger.GetApplicationDirectory(), Is.EqualTo("C:\\Windows\\Fenestra"));
Assert.That(logger.GetLoggerInstance, Is.EqualTo("GetLoggerInstance"));
Assert.That(logger.GetDirectoryForDependencyByLoggerName
("CustomLogger"), Is.EqualTo("C:\\Windows\\Temp"));
- Usage of
MockRepository
for setting behavior of few mock objects. Suppose you have more complicated class WizzLogger
, which needs two more dependencies: ILogSaver
and ILogMailer
. Our testing class with calling its method Write
should call methods of those two dependencies.
For example, like this:
public interface ILogSaver
{
string GetLogger();
void SetLogger(string logger);
void Write(string message);
}
public interface ILogMailer
{
void Send(MailMessage mailMessage);
}
public class WizzLogger
{
private ILogMailer mailer;
private ILogSaver saver;
public WizzLogger(ILogSaver s, ILogMailer m)
{
mailer = m;
saver = s;
}
public void Send(MailMessage mailMessage) { }
public void WriteLine(string message)
{
mailer.Send(new MailMessage());
saver.Write(message);
}
}
then in your test, you can write like this:
var repo = new MockRepository(MockBehavior.Default);
var logWriterMock = repo.Create<ILogSaver>();
logWriterMock.Setup(lw => lw.Write(It.IsAny<string>()));
var logMailerMock = repo.Create<ILogMailer>();
logMailerMock.Setup(lm => lm.Send(It.IsAny<MailMessage>()));
var WizzLogger = new WizzLogger(logWriterMock.Object, logMailerMock.Object);
WizzLogger.WriteLine("Hello, Logger");
repo.Verify();
Other Ways
In some cases, it can be useful to get Mock
object itself according to interface (get Mock<ISomething>
of interface ISomething
). For example, functional syntax of stubs initialization returns not Mock
object, but required interface immediately. This can be convenient for testing pair of simple methods, but not convenient if you also need to check behavior or configure method, which gives different results for different parameters. As outcome sometimes it's easier to use LINQ-based syntax for one part of methods and use Setup
methods for other part of methods:
ILoggerSomeDependency logger = Mock.Of<ILoggerSomeDependency>
(ld => ld.GetApplicationDirectory() == "C:\\Windows\\Fenestra"
&& ld.GetLoggerInstance == "GetLoggerInstance");
Mock.Get(logger)
.Setup(ld => ld.GetDirectoryForDependencyByLoggerName(It.IsAny<string>()))
.Returns<string>(loggerName => "C:\\" + loggerName);
Assert.That(logger.GetApplicationDirectory(), Is.EqualTo("C:\\Windows\\Fenestra"));
Assert.That(logger.GetLoggerInstance, Is.EqualTo("GetLoggerInstance"));
Assert.That(logger.GetDirectoryForDependencyByLoggerName("Foo"),
Is.EqualTo("C:\\Merced"));
Assert.That(logger.GetDirectoryForDependencyByLoggerName
("Itanium"), Is.EqualTo("C:\\Itanium"));
Besides this, Mock allows to check behavior of protected
methods, test events and contains other features. But that can be a topic for another article.
If you are confused in tests, I've attached an implementation of Interfaces and some classes.