Rhino Mocks Limitations I'm getting a lot of questions recently about Rhino Mocks, and I wanted to address the common one here. Rhino Mocks at the moment has two major limitations:
These two issues are known problems in Dynamic Proxy, both should be addressed in the next release of Dynamic Proxy (2.0), which is currently under work. Beyond that, of course, there is the limitation of being able to mock only methods / properties / events that are marked as virtual. This is not something that is likely to change. |
Rhino Mocks And Interfaces With Generics Methods I the last week I was asked multiply times about using Rhino Mocks to mock interfaces with generic methods. At the moment, this is not working, the code needed to generate generic methods on runtime is not trivial, and I tried fixing it several times, without success. Currently, Dynamic Proxy 2 is being worked on, which will have support for generic methods, so I expect to have this feature soon. The following hack will get you by for now, it is not ideal, but it is what I have at the moment. The ISession interface from NHibernate contains a generic Load<T>(object id) method, which measn that Rhino Mocks can't mock it. Here is how we solve the issue: public abstract class SessionWithoutGenerics : ISession
{
public override sealed Load<T>(object id)
{
return (T)Load(typeof(T), id);
}
}
We basically disable the mocking of this specific method, and now we mock this class, and continue life as usual.
|
Advnace Mocking Scenarios With Active Record (using Rhino Mocks) My recent post sparked some discussion in the Castle Mailing List, with some question of how to handle more complex scenarios. I managed to code those without much problem, as you can see below, but a few words first. This method essentially mocks NHibernate for Active Record. This means that you have to understand what is happening (which is not a bad idea). I'm intimately familiar with both Active Record and NHibernate, and I had to look at the source to see what was happening. If you intend to use this method, I highly recommend that you'll not put the expectations in the test itself, but move the expectation building to the MockScope, so you will be able to do something like:
mockedScope.OnFind(typeof(Blog),30).Return(mockedBlog);
Update: Added examples for both 2.0 and 1.1 Here are the examples (.Net 2.0): [TestFixture]
public class Usage
{
MockRepository mocks;
ISession session;
[TestFixtureSetUp]
public void Initialize()
{
IConfigurationSource source = System.Configuration.ConfigurationManager.GetSection("activerecord") as IConfigurationSource; //this for .net 2.0
ActiveRecordStarter.Initialize(typeof(Blog).Assembly, source);
}
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
session = mocks.CreateMock<ISession>();
}
[TearDown]
public void TearDown()
{
mocks.VerifyAll();
}
[Test]
public void UsingMockedScope()
{
Blog mockedBlog = new Blog();
Expect.Call(session.Load(typeof(Blog), 30)).Return(mockedBlog); ;
mocks.ReplayAll();
using (new MockScope(session))
{
Blog blogFromDatabase = Blog.Find(30);
Assert.AreSame(mockedBlog, blogFromDatabase);
}
}
[Test]
public void UsingMockedScope_WithFindAll()
{
Blog mockedBlog = new Blog();
IList returnedList = new ArrayList();
returnedList.Add(mockedBlog);
ICriteria criteria = mocks.CreateMock<ICriteria>();
Expect.Call(session.CreateCriteria(typeof(Blog))).Return(criteria);
Expect.Call(criteria.List()).Return(returnedList);
mocks.ReplayAll();
using (new MockScope(session))
{
Blog[] blogs = Blog.FindAll();
Assert.AreEqual(1, blogs.Length);
Assert.AreSame(mockedBlog, blogs[0]);
}
}
[Test]
public void UsingMockedScope_WithFindByProperty()
{
Blog mockedBlog = new Blog();
IList returnedList = new ArrayList();
returnedList.Add(mockedBlog);
ICriteria criteria = mocks.CreateMock<ICriteria>();
Expect.Call(session.CreateCriteria(typeof(Blog))).Return(criteria);
Expect.Call(criteria.Add(null)).Constraints(
Property.Value("Value", "Ayende @ Blog") & Property.Value("PropertyName", "Name")
).Return(criteria);
Expect.Call(criteria.List()).Return(returnedList);
mocks.ReplayAll();
using (new MockScope(session))
{
Blog[] blogs = Blog.FindAllByProperty("Name", "Ayende @ Blog");
Assert.AreEqual(1, blogs.Length);
Assert.AreSame(mockedBlog, blogs[0]);
}
}
[Test]
public void UsingMockedScope_WithSimpleQuery()
{
Blog mockedBlog = new Blog();
IList returnedList = new ArrayList();
returnedList.Add(mockedBlog);
IQuery query = mocks.CreateMock<IQuery>();
Expect.Call(session.CreateQuery("from Blog b where b.Name = ?")).Return(query);
Expect.Call(query.SetParameter(0, "Ayende @ Blog")).Return(query);
Expect.Call(query.List()).Return(returnedList);
mocks.ReplayAll();
using (new MockScope(session))
{
SimpleQuery<Blog> q = new SimpleQuery<Blog>("from Blog b where b.Name = ?", "Ayende @ Blog");
Blog[] blogs = q.Execute();
Assert.AreEqual(1, blogs.Length);
Assert.AreSame(mockedBlog, blogs[0]);
}
}
} And here are the examples for .Net 1.1: [TestFixture]
public class Usage
{
MockRepository mocks;
ISession session;
[TestFixtureSetUp]
public void Initialize()
{
IConfigurationSource source = System.Configuration.ConfigurationManager.GetSection("activerecord") as IConfigurationSource; //this for .net 2.0
ActiveRecordStarter.Initialize(typeof(Blog).Assembly, source);
}
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
session = (ISession)mocks.CreateMock(typeof(ISession));
}
[TearDown]
public void TearDown()
{
mocks.VerifyAll();
}
[Test]
public void UsingMockedScope()
{
Blog mockedBlog = new Blog();
Expect.Call(session.Load(typeof(Blog), 30)).Return(mockedBlog); ;
mocks.ReplayAll();
using (new MockScope(session))
{
Blog blogFromDatabase = Blog.Find(30);
Assert.AreSame(mockedBlog, blogFromDatabase);
}
}
[Test]
public void UsingMockedScope_WithFindAll()
{
Blog mockedBlog = new Blog();
IList returnedList = new ArrayList();
returnedList.Add(mockedBlog);
ICriteria criteria = (ICriteria)mocks.CreateMock(typeof(ICriteria));
Expect.Call(session.CreateCriteria(typeof(Blog))).Return(criteria);
Expect.Call(criteria.List()).Return(returnedList);
mocks.ReplayAll();
using (new MockScope(session))
{
Blog[] blogs = Blog.FindAll();
Assert.AreEqual(1, blogs.Length);
Assert.AreSame(mockedBlog, blogs[0]);
}
}
[Test]
public void UsingMockedScope_WithFindByProperty()
{
Blog mockedBlog = new Blog();
IList returnedList = new ArrayList();
returnedList.Add(mockedBlog);
ICriteria criteria = (ICriteria) mocks.CreateMock(typeof(ICriteria));
Expect.Call(session.CreateCriteria(typeof(Blog))).Return(criteria);
Expect.Call(criteria.Add(null)).Constraints(
Property.Value("Value", "Ayende @ Blog") & Property.Value("PropertyName", "Name")
).Return(criteria);
Expect.Call(criteria.List()).Return(returnedList);
mocks.ReplayAll();
using (new MockScope(session))
{
Blog[] blogs = Blog.FindAllByProperty("Name", "Ayende @ Blog");
Assert.AreEqual(1, blogs.Length);
Assert.AreSame(mockedBlog, blogs[0]);
}
}
[Test]
public void UsingMockedScope_WithSimpleQuery()
{
Blog mockedBlog = new Blog();
IList returnedList = new ArrayList();
returnedList.Add(mockedBlog);
IQuery query = (IQuery)mocks.CreateMock(typeof(IQuery));
Expect.Call(session.CreateQuery("from Blog b where b.Name = ?")).Return(query);
Expect.Call(query.SetParameter(0, "Ayende @ Blog")).Return(query);
Expect.Call(query.List()).Return(returnedList);
mocks.ReplayAll();
using (new MockScope(session))
{
IActiveRecordQuery q = new SimpleQuery(typeof(Blog),"from Blog b where b.Name = ?", "Ayende @ Blog");
Blog[] blogs = (Blog[])q.Execute(session);
Assert.AreEqual(1, blogs.Length);
Assert.AreSame(mockedBlog, blogs[0]);
}
}
}
|