views:

340

answers:

5

I have repository class that is used by at least 2 other classes. This repository class needs to be initialized - which is high in cost (querying database). Now, I create separate instances of repository wherever I need it. The thing is, that everytime I create repository it has to be initialized. How to design such repository to be TDD-friendly? The first thing in my mind was Singleton but it's not the solution.

+1  A: 

Do you use any type of IOC container? Unity is my container of choice, and it contains a ContainerControledLifetimeManager which makes your class a singleton, but not managed by yourself.

Mladen Mihajlovic
+1  A: 

Consider caching instances for performance improvement before you consider singletons. But for TDD friendly designs consider strategy injection so that 'slow' bits can be removed for testing and replaced with stubs and mocks. Try not to do db calls in tests if you can.

Preet Sangha
+1  A: 

One answer for the TDD part is learn mocking.

Check out this excellent article by Stephen Walther:

http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx

IainMH
Down voter. Pls explain why. Have I misunderstood the question?
IainMH
The article you provided is good, but your answer is about mocking. My question is about design. "How to design that I could test it with mocks?"
rafek
Actually his answer is quite right
Jon Limjap
It's not right, because it doesn't address the design problem. What it does is advice to use mocking. "How to design such repository to be TDD-friendly?" - not "How to test such repository?"
rafek
What you want to do is NOT TDD.
Jon Limjap
Yeah, the irony here is that you can't design something to be TDD friendly - you design it via TDD, therefor whatever you create is by default TDD friendly... You're not doing TDD if you're doing up-front design.
Erik Forbes
+3  A: 

I hope by TDD-friendly you mean 'testable' code. For a Singleton ObjectX, I think the most common way is to split the responsibility (SRP) of 'controlling creation' to another class so ObjectX does all the things it is supposed to do.

Then you have another class ObjectXFactory or Host or whatever you wanna call it that is responsible for providing a single instance for all clients (and providing thread sync if needed and so on)

  • Object X can be TDDed independently. You can create a new instance in your test case and test functionality.
  • ObjectXFactory on the other hand is also easy to test.. you just need to see if multiple GetInstance() calls return the same object. OR better delegate this responsibility to an IOC framework like Spring, which lets you declaratively mark an object definition to obtain singleton behavior (Saving you the effort of writing tests as well)

You just need to educate and conform to a Team convention that ObjectX constructor is not to be called - always use ObjectXFactory.CreateInstance(). (If you find that you have a awareness/discipline problem, mark ObjectX's ctor as internal and visible to only to the test assembly via the sneaky InternalsVisibleToAttribute) HTH

Gishu
+1  A: 

You can't do that -- at least not in a true TDD sense.

Relying on DI/IoC strategies such as Unity means your tests are dependent on an external component and are not tested in isolation.

The tests then become integration tests, not unit tests.

==Ignore the answer below here==

I guess you wanted to know how to make Repository testable.

Introducing an interface for it would allow you to mock or stub it, which will in turn make sure that you can test your objects independent of any concrete implementation of Repository.

I'll illustrate this using Rhino Mocks 3.5 for .NET 3.5:

Let's make an interface out of Repository, let's call that IRepository

public interface IRepository
{
}

Now, since you need to use IRepository for two different objects, then let's just use generics so you can instantiate your repository with that:

public interface IRepository<T>

of course that would mean that you would have some sort of find method:

{
    public IEnumerable<T> Find(Criteria criteria)
}

where your criteria object is some object that allows you to set what to look for, e.g., your where clause.

Now, you have your object:

public class SomeObject
{
    IRepository<SomeObject> repository;

    public SomeObject(){}

    public IRepository<SomeObject> repository { get; set; }

    IEnumerable<SomeObject> FindAll()
    {
        //let's assume new Criteria() will return all results
        return respository.Find(new Criteria());
    }
}

You want to to test SomeObject such that FindAll() will return an expected set of results -- this is where Rhino Mocks would come in:

[TestFixture]
public class SomeObjectTests
{
    [Test]
    public void TestSomeObjectFindAll()
    {
        IRepository<SomeObject> stubRepository = MockRepsitory.GenerateStub<IRepsitory<SomeObject>>();

        stubRepository.Expect(r => r.Find(new Criteria())
            .Return(new List<SomeObject>{ 
                        new SomeObject(), 
                        new SomeObject(), 
                        new SomeObject());

        var testObject = new SomeObject { Repository = stubRepository };
        IList<SomeObject> findAllResult = testObject.FindAll();

        //returned list contains 3 elements, as expected above
        Assert.AreEqual(3, findAllResult.Count)
    }
}

Note that the code above is not TDD best practice in all respects, but it's a place to start.

Key concept here is introducing interfaces to allow for loose coupling of objects, especially when the object tends to do things like access databases, file systems, etc.

There is a more comprehensive example and better examples on Ben Hall's article on Rhino Mocks.

Jon Limjap
Same with your answer.
rafek
Don't you realize that I actually answered your question? Didn't you read the part where I answered that you have to use Interfaces? Is it really our fault if you don't like to hear about mocking?
Jon Limjap
It's not the case. I love mocking. The problem was how to implement Repository that acts like Singleton but is testable.
rafek
Edited my answer to suit your conditions. ;)
Jon Limjap
And yes I can, apparently asnwer from Mladen Mihajlovic helped me. I'm just trying Unity and it seems good for me.
rafek
Perhaps, but that's not TDD; because you're already relying on Unity for setting up your tests, what you're doing is integration testing.
Jon Limjap
I'm not sure if you're right. Saying that I'm relying on Unity is the same as saying that I'm relying on log4net or nunit or anything else. The case is that Unity (or any other IoC container) helps me decouple some classes and that is the solution for me.
rafek
Mocking was invented precisely because of these scenarios, rafek. I'm not sure you understand that.
Jon Limjap