As a general rule, when creating unit test, you shouldNOT
have the test code communicate directly across the network, to the
database, the file system, etc. This is a good thing since it will
allow the unit test to be fast and remove the headache of doing test
data management, etc. For more details on what a unit test should not
do, see here
. To achieve this goal, mocking and stubbing
were introduced into the unit testing world.
Stubbing
Stubbing will allow you to create fake dependency objects that you
can use to give the class that you are unit testing constant / static
values, something that will not change across tests. An example of this
is when you want to create a fake data repository from a data
repository interface that will return you a set of pre-canned (always
the same) values when queried.
var warehouse = new
Mock<IWarehouse>();
warehouse.SetupGet(w => w.HasInventory).Returns(false
);
Fig 1. Example of a warehouse stub using Moq (warehouse stub will return false when it’s HasInventory property is invoked).
Mocking
Mocking will allow you to create fake dependency objects that contain
expectations that you can validate as part of your unit test (and also
can provide what stub can provide). An example of this is when you want
to determine if a particular branch or path is taken when a certain
parameter is passed to your method.
[TestMethod]
public
void
Should_SendMail_If_Unfilled() {
//Assign
var warehouse = new
Mock<IWarehouse>();
var mailer = new
Mock<IMailService>();
warehouse.SetupGet(w => w.HasInventory).Returns(false
);
var order = new
Order
{
ItemName = "Boo"
,
Amount = 51,
Mailer = mailer.Object
};
//Act
order.Fill(warehouse.Object);
//Assert
mailer.Verify(m => m.Send(It.IsAny<Message>()), Times.Once());
Fig 2. Example of using mock to verify mailer Send method was called once when trying to fullfill an order using Moq.
For more information on the difference between mock and stub, see this
.
Perhaps this is old news, but like there are a lot of unit testing frameworks out there,i.e. NUnit
, xUnit
, MSTest
, etc.
, there are also a bunch of mocking frameworks out there, like RhinoMock
, Moq
, TypeMock Isolator
, and recently JustMock from Telerik
.
To do proper mocking or stubbing, and by extension, unit testing, you
usually need the following things: interface and dependency injection.
Interface
Interface will allow you to switch one implementation of it with a
different implementation. I.e. SqlRepository, that derives from an
IRepository, will actually talk to a SQL database backend, while a
FakeRepository that derives from the same IRepository might actually
fake the database call and instead returns an in memory list of data
when asked. The ability to switch between a real implementation and a
fake implementation is a crucial thing if you want to make your unit
test fast.
Dependency Injection
Before we go too far into the topic of dependency injection, let’s
take a quick detour. The Dependency Inversion Principle stated that
higher level object should not depend on the lower level objects,
instead, have all of them depend on a contract or interface /
abstraction.
Think about how a desk lamp and electric socket interact. The
electric socket can provide electricity to any devices (not just the
lamp) that can connect to it. As long as the specific device that wants
to get electricity implement the right plug, then it’s no problem for
the socket to provide it with electricity. Compare this design versus
lamps that are built into the wall and wired directly to the electrical
wiring. If you want to switch the lamp, you have to tear down your
wall, rip out the old lamp, install a new lamp, wire it up to the
electrical wiring in the wall and redo your wall. What a pain… With
electrical socket, I can just unplug the lamp and replace it with a TV
for example, and it still work. The electrical socket can still provide
electricity with very minimum interruption.
Again, this will allow you to easily swap one implementation with another for the same contract / interface. See here
to see more detail on this topic.
public
interface
IElectricalDevice
{
void
SwitchOff();
}
public
class
Lamp : IElectricalDevice
{
public
void
SwitchOff()
{
//It's dark!
}
}
public
class
Refridgerator : IElectricalDevice
{
public
void
SwitchOff()
{
//Wow it's getting warm in here...
}
}
public
class
ElectricalOutlet
{
private
IElectricalDevice _pluggedInDevice;
public
ElectricalOutlet(IElectricalDevice pluggedInDevice)
{
_pluggedInDevice = pluggedInDevice;
}
public
void
PowerOff()
{
_pluggedInDevice.SwitchOff();
}
}
Fig 3. Example of electric socket and devices as code
Now that we see why DIP is important, comes the question of how can
we “inverse” the dependency to make our solution more loosely coupled?
That’s where injection comes in. You need to provide points in your
object where you can inject other objects that it need to do its work.
Most common points are the class constructor and public properties.
In Fig. 3 code above, I was using constructor injection in the
ElectricalOutlet class (the constructor takes an IElectricalDevice
object, thus injecting it into the ElectricalOutlet class). Property is
self explanatory. Since you can inject different instance of object of
its type during the application runtime.
Moles
Now, let’s talk about moles.
What a tunneling blind rodent has to do with this, you ask…
Well, not that kind of mole.
Not the other mole either (WPF debugger)
. I am talking about a unit testing kind of moles. Moles
is a mocking framework from Microsoft Research group with similar
ability that TypeMock Isolator or JustMock offered. Just like the other
products, it will allow you mock stuffs that previously unmockable if
you are using mocking frameworks like RhinoMock or Moq. Unlike TypeMock
and JustMock, Moles is a free product. I supposed the name was chosen
due to the ability of the framework to dig deep into a class and replace
whatever you wish to replace with your own implementation that you can
use during unit testing.
Moles provides 2 types of capabilities: stubs and moles.
Stub is a very fast compared to moles but has certain limitation,
mostly you can only stub things that has interfaces and class instances
with virtual methods, similar to RhinoMock and Moq.
Mole is not as fast as stub since it injects instrumentation code
into your actual classes and redirect control to itself when called.
The benefit of this method is that now it can totally take control of
any sealed class, static methods, etc. that are usually not mockable /
stubable without a proper interface or virtual methods.
// let's detour DateTime.Now (Mole into DateTime.Now property, which usually not mockable w/ other framework)
MDateTime.Now = () => new
DateTime(2000, 1, 1);
if
(DateTime.Now == new
DateTime(2000, 1, 1))
throw
new
Y2KBugException(); // take cover!
Fig 4. Using Moles to return 1 Jan 2000 everytime DateTime.Now is called.
Having said that, currently Moles does not have internal mocking
support like RhinoMock and Moq. To do mocking with Moles, you will have
to implement them yourself.
[TestMethod]
[HostType("Moles"
)]
public
void
Should_SendMessage_Once_When_Not_Filled()
{
//Assign
var mailSendCallCount = 0;
//Create stubs (S prefix = stub)
var warehouse = new
SIWarehouse();
var mailer = new
SIMailService();
//Setup warehouse stub to return false
//when its HasInventory property is called
warehouse.HasInventoryGet = () => false
;
//Setup mailer stub to increase the mailSendCallCount
//everytime SendMessage is called
mailer.SendMessage = (m) => { mailSendCallCount++; };
var order = new
Order
{
ItemName = "Boo"
,
Amount = 51,
Mailer = mailer
};
//Act
order.Fill(warehouse);
//Assert
Assert.AreEqual(1, mailSendCallCount);
}
Fig 5. Example of mocking with Moles
Resources
To learn more detail on Moles, I suggest you check the following resources:
http://www.dimecasts.net/Casts/CastDetails/170
(good short video on introduction to Moles)
http://research.microsoft.com/en-us/projects/pex/molesmanual.pdf
Happy Unit Testing & Moling? Is that a verb?
分享到:
相关推荐
在"Unit Testing with Microsoft Moles"的例子中,我们将探讨如何使用Moles框架来创建和执行单元测试。Moles框架的核心理念是“桩”(stubs)和“莫尔斯”(moles),其中桩用于替代对象实例,而莫尔斯则用于替代...
- **Mocking Objects**:通过Mockito可以轻松创建mock对象,并定义其行为。 - **Verifying Interactions**:可以在测试中验证对象之间的交互是否按预期进行。 - **Spying on Real Objects**:如果需要部分模拟某个...
单元测试(Unit Testing)是软件开发过程中的一个重要环节,它主要针对程序代码的最小可测试单元进行验证,确保每个函数、方法或类的行为都符合预期。在“WF”(Windows Workflow Foundation)的上下文中,单元测试...
6. **Mocking和Stubbing**:在复杂的系统中,单元测试往往需要模拟(mocking)或存根(stubbing)外部依赖,以便隔离被测试的代码。Java有多种库如Mockito、EasyMock等支持这些功能。 7. **持续集成(CI)**:单元...
另一个文件"Pragmatic.Bookshelf.Pragmatic.Unit.Testing.in.C.Sharp.with.NUnit.2nd.Edition.Aug.2007.eBook-BBL"则是电子书本身,包含了详细的章节内容和深入的讨论。 总的来说,《单元测试之道C#版:使用NUnit》...
9. Mocking和Stubbing:在单元测试中,我们常常需要模拟(Mock)或桩(Stub)外部依赖,以隔离测试。JUnit本身并不提供这些功能,但可以配合Mockito等库来实现。 10. TDD(Test-Driven Development)和BDD...
This short step by step guide walks you through unit testing, mocking and continuous integration and how to get these key ingredients running in your Swift projects. This book also looks at how to ...
书中会探讨模拟对象(mocking)、存根(stubbing)和隔离框架(如Moq和Rhino Mocks)的使用。 5. **测试模式和最佳实践**:书中涵盖了各种测试模式,如测试金字塔、 Arrange-Act-Assert模式,以及如何编写可读性强...
Whether you are new to JUnit testing and mocking or a seasoned Mockito expert, this book will provide you with the skills you need to successfully build and maintain meaningful JUnit test cases and ...
* simplifies testing by supporting unit testing and mocking out-of-the-box * seamlessly integrates with all existing Java classes and libraries * compiles straight to Java bytecode so you can use ...
《The Art of Unit Testing》是由 Roy Osherove 编著的一本关于单元测试的重要书籍,出版于2009年,由Manning出版社发行。这本书深入探讨了单元测试的理论与实践,是软件开发人员尤其是测试人员的重要参考资料。在...
Mocking库如Moq(C#)和Mockito(Java)可以帮助我们创建模拟对象,以便在测试中控制它们的行为和预期结果。 6. **代码覆盖率**: 为了评估测试的全面性,我们通常会关注代码覆盖率,即测试执行了多少代码。工具...
"UnitTesting-Swift-Practices"项目提供了一系列小型项目,旨在通过实践来深入理解Swift中的单元测试。下面将详细讨论Swift单元测试的基本概念、重要性、常用工具以及如何进行有效的单元测试实践。 1. **单元测试...
- Detailed explanation of mocking and stubbing techniques to isolate parts of the system under test and control external dependencies. - Practical examples demonstrating when and how to use mocks ...
Use mocking frameworks and techniques to easily write and quickly execute tests Develop an application to implement behaviour-driven development in conjunction with unit testing Enable and disable ...