Lazy Loading/Instantiation testing with mocks

195 views
Skip to first unread message

Tariq

unread,
Feb 25, 2008, 4:53:44 PM2/25/08
to Rhino.Mocks

Hi,

Ive been looking on the web for an answer and cant seem to figure it
on my own.

If you have the simple scenario of business objects relating to other
business objects, such as employee-> employee groups , what is the
right way to architect it for testing ?

I understand that it should be using dependency based injection but im
not sure that this architecture suits a model that uses lazy loading.
Does this mean that i shouldnt use lazy loading, if so whats the
alternative because this is every where in my code. Also since im
mocking, do i pass in every related collection for an object?
I think im missing something major in my understanding, any help
appreciated.

E.g I want to test employee.EmployeeGroups fucntionality is working

How would i test this for my current architecture( or what should it
be changed to ?):

public interface IEmployee
{
IEmployeeGroupCollection EmployeeGroups { get; }
}

public class Employee : IEmployee
{
private IEmployeeGroupCollection _employeeGroups;
public IEmployeeGroupCollection EmployeeGroups
{
get
{
if (_employeeGroups == null)
{
_employeeGroups = new
EmployeeEmployeeGroupCollection(this.EmployeeID);
}
return _employeeGroups;
}
}
}



nmalovic

unread,
Feb 25, 2008, 5:25:52 PM2/25/08
to Rhino.Mocks
Usually I would mock the EmployeeGroupCollection and check in test if
it is called only once, but I don't know how you can write expectation
for constructor (I guess it is possible)

I'm sure plenty of people here would fill that gap :)
> }- Hide quoted text -
>
> - Show quoted text -

Owen Evans

unread,
Feb 25, 2008, 5:30:06 PM2/25/08
to Rhino...@googlegroups.com
So just going on your example, take a look at what the system under test should do... one of two things,
a) get a new EmployeeGroupsCollection if none exists
b) return the original EmployeeGroupsCollection if it existed already

so these can be boiled down to doing the following:
[TestFixture]
public void GivenANewEmployeeGroupsCollecti
on()
{
private IEmployee employee;
[Test]
public void TheEmployeeShouldLoadGroupsWhenRequested()
{
employee = new Employee();
Assert.IsNotNull(employee.EmployeeGroups);
}
[Test]
public void TheEmployeeShouldReturnTheSameGroupIfRequestedAgain()
{
employee = new Employee();
Assert.AreSame(employee.EmployeeGroups,employee.EmployeeGroups);
}
}



now if you look at employee groups and want to mock it out (reasons for this are that perhaps the EmployeeGroups class does a load of calls on construction, or is just plain slow), then perhaps the easiest way is to use a FactoryClass.

interface IEmployeeGroupFactory
{
IEmployeeGroupCollection Create(Guid EmployeeID);
}


then you can pass in the employeeGroupfactory with your constructor (or a DI framework with the actual application)
and you don't have to worry about what EmployeeGroupCollection does when it's instantiated

your tests would look like this:

[TestFixture]
public void GivenANewEmployeeGroupsCollection()
{
private IEmployee employee;
private MockRepository mocks = new MockRepository();
[Test]
public void TheEmployeeShouldLoadGroupsWhenRequested()
{
IEmployeeGroupFactory factory= mocks.CreateMock<IEmployeeGroupFactory>();
employee = new Employee(factory);
IEmployeeGroupCollection groups = mocks.CreateMock<IEmployeeGroupCollection>();
using(mocks.Record())
{
//this should call the create method
Expect.Call(factory.Create(employee.ID)).Returns(groups);
}
using(mocks.Playback())
{
Assert.AreEqual(groups,employee.EmployeeGroups);
}
[Test]
public void TheEmployeeShouldReturnTheSameGroupIfRequestedAgain()
{
IEmployeeGroupFactory factory= mocks.CreateMock<IEmployeeGroupFactory>();
employee = new Employee(factory);
IEmployeeGroupCollection groups = mocks.CreateMock<IEmployeeGroupCollection>();
using(mocks.Record())
{
//this should call the create method but only get called once.
Expect.Call(factory.Create(employee.ID)).Returns(groups);
}
using(mocks.Playback())
{
Assert.AreEqual(employee.EmployeeGroups,employee.EmployeeGroups);
}
}


does that make any sense?

Cheers
Owen

Tariq

unread,
Feb 25, 2008, 6:08:45 PM2/25/08
to Rhino.Mocks
Hi Owen,

Thanks, that makes alot of sense, I especially like the
TheEmployeeShouldReturnTheSameGroupIfRequestedAgain method.
By using the object with a factory passed in you would test that the
Create gets called. Then there are other tests for factory.Create to
check that it works properly, that's the separation of
responsibilities.

I got a couple of questions though for clarification:

- So by this design , does this mean that I should have at the maximum
have a factory for every related collection or object, or at the least
have one factory, e.g a IEmployeeRelatedObjectsFactory which would
have every Create for every collection or related object ?
- Secondly, as a result of a factory being passed in this means that
the UI would have a factory/factories for every business object that
is being used. This seems a bit much because the UI shouldn't care
about how or what the business objects uses to get its relations or
carry out its functions. How would the UI change as a result ? Is
there another layer ?
- Just as a side question, Im guessing the easiest refactoring method
would be to pass the factory into the collection constructor or
related object ?

Thanks again,
Tariq

On Feb 26, 9:30 am, "Owen Evans" <owenmcev...@gmail.com> wrote:
> So just going on your example, take a look at what the system under test
> should do... one of two things,
> a) get a new EmployeeGroupsCollection if none exists
> b) return the original EmployeeGroupsCollection if it existed already
>
> so these can be boiled down to doing the following:
> [TestFixture]
> public void GivenANewEmployeeGroupsCollection()
> {> private IEmployee employee;

Owen Evans

unread,
Feb 25, 2008, 6:28:29 PM2/25/08
to Rhino...@googlegroups.com
Yep the whole idea of mocking is to concentrate the tests on a single unit of code without worrying how the rest is done.



- So by this design , does this mean that I should have at the maximum
have a factory for every related collection or object, or at the least
have one factory, e.g a IEmployeeRelatedObjectsFactory which would
have every Create for every collection or related object ?

Certain things come down to personal choice. i.e. perhaps you could have a generic factory that does the create for you and then you just use a version of the factory for the correct type of object/collection.


- Secondly, as a result of a factory being passed in this means that
the UI would have a factory/factories for every business object that
is being used. This seems a bit much because the UI shouldn't care
about how or what the business objects uses to get its relations or
carry out its functions. How would the UI change as a result ? Is
there another layer ?

I'm not sure why your UI would have to know how to get "business objects" personally I follow an MVP pattern or an MVC pattern and the loading of objects is controlled by the presenter or controller respecivly, as such each controller/presenter constructor has the factories that it will need passed in through the constructor.. well at least that's how i would do it but generally I have a repository pattern which means i just pass in an instance of the repository and let the repository work out what factory to call (Repository.GetByID<PersonCollection>(PersonID) for example) that might be the best use here, just introduce a helper repository that knows how to resolve loads, creates and adds. OR use an existing ORM mapper to get this goodness for free.


- Just as a side question, Im guessing the easiest refactoring method
would be to pass the factory into the collection constructor or
related object ?
I'm really sorry but i'm not sure i understand this question. :-S. If your creating a new object it should have it's dependancies passed to it, and not create a new object, so yes the factory should pass in a new factory instance into the object... but generally I would just have a repository pattern with generics so you only really have one standard factory passing around (the repository). 

Hoping these were the answers you were looking for

Cheers
Owen
Reply all
Reply to author
Forward
0 new messages