If
you haven't delved into the wonderful world of unit testing and mock
objects, here is a quick summary of my current recommended approach to
using Rhino
Mocks, my current mock testing
framework of choice.
Why
Rhino Mocks?
It is the most flexible mocking framework I've found and it is easy
enough to use once you understand how. (there is a slight
learning curve)
The two main types of
mocks that I recommend:
StrictMock
DynamicMock
StrictMock
What:
Will fail if anything unexpected occurs
When
To Use: When adding unit tests
to pre-existing code, especially unfamiliar code
Pro:
High confidence that code is
thoroughly tested
Con:
Tests are more coupled to the methods that they test
DynamicMock
What:
Will ignore unexpected function calls and will only fail if explicit
expectations fail
When
To Use: When writing new code
using Test-Driven Development
Pro:
Less coupling
Con:
Potential for false positives
Here
is example code showing how to use both. Try stepping through
the code in visual studio's debugger to see how it works!
public interface IMath
{
void Init();
int Mult(int i, int j);
}
public class Class1
{ // (this is the method that is being tested in this example!!!)
public int DoIt(IMath m, int x, int y)
{
m.Init();
return m.Mult(x, y);
}
}
[TestClass()]
public class Class1Test
{
[TestMethod()]
public void MultiplyTestStrict()
{
MockRepository mocks = new MockRepository();
IMath mockMath = mocks.StrictMock<IMath>();
using (mocks.Record())
{
mockMath.Stub(x => x.Init()); // must be included since it is a strict mock
mockMath.Expect(x => x.Mult(Arg<int>.Is.Equal(2), Arg<int>.Is.Equal(2))).Return(4);
}
using (mocks.Playback())
{
Class1 instance = new Class1();
int iResult = instance.DoIt(mockMath, 2, 2);
Assert.AreEqual(iResult, 4);
}
}
[TestMethod()]
public void MultiplyTestDynamic()
{
MockRepository mocks = new MockRepository();
IMath mockMath = mocks.DynamicMock<IMath>();
using (mocks.Record())
{
// don't need to explicitly include Init() here because any unknown function calls will be ignored
mockMath.Expect(x => x.Mult(Arg<int>.Is.Equal(2), Arg<int>.Is.Equal(2))).Return(4);
}
using (mocks.Playback())
{
Class1 instance = new Class1();
int iResult = instance.DoIt(mockMath, 2, 2);
Assert.AreEqual(iResult, 4);
}
}
[TestMethod()]
public void MultiplyTestDynamicAlternateSyntax()
{
var mockMath = MockRepository.GenerateMock<IMath>();
// arrange
mockMath.Expect(x => x.Mult(2, 2)).Return(4);
// act
Class1 instance = new Class1();
int iResult = instance.DoIt(mockMath, 2, 2);
// assert
mockMath.VerifyAllExpectations();
Assert.AreEqual(4, iResult);
}
}
For ordered expectations:
MockRepository repository = new MockRepository();
using (repository.Ordered())
{
// set some ordered expectations
}
repository.ReplayAll();
using (repository.Playback())
{
// test
}
To accept arbitrary parameter but still act upon it:
mockThreadFactory.Expect(x => x.Start(Arg<ParameterizedThreadStart>.Is.Anything, Arg<DBExportLib.Util.PackageUpXMLThreadComm>.Is.Anything, Arg<string>.Is.Anything))
.Return(mockThread)
.WhenCalled(invocation =>
{
DBExportLib.Util.PackageUpXMLThreadComm comm = invocation.Arguments[1] as DBExportLib.Util.PackageUpXMLThreadComm;
comm.m_bSuccess = true;
}
);
To compare the contents of two lists for equality:
(unit test related, not necessarily related to mocks but I use this all the time)
Assert.IsTrue(actual.SequenceEqual(expected));
(requires "using System.Linq" at the top of the .cs file, no reference apparently needed)
See http://ayende.com/wiki/Rhino+Mocks+3.5.ashx for a more thorough reference.