views:

732

answers:

3

While using Repository pattern I find it difficult to understand the reason of designing the software with TDD technique while in reality you will have to implement the Interface for your repository in your persistence dataset.

To make my point clear I will submit an example:

I have the following Interface on my domain model:

public interface IUserRepository
{
    IQueryable<User> FindAllUsers();
    void AddUser(User newUser);
    User GetUserByID(int userID);
    void Update(User userToUpdate);
}

I have the following implementation of the interface for testing purposes:

public class FakeUserRepository : IUserRepository
{
    private IList<User> _repository;

    public FakeUserRepository()
    {
        _repository = new List<User>();
        ... //create all users for testing purposes

    }

    public IQueryable<User> FindAllUsers()
    {
        return _repository.AsQueryable<User>(); //returns all users
    }

Now I create a few tests:

  1. Can_Add_User
  2. Can_Add_Account for a User
  3. Can_Add_ShareSpace for a an account of a user with another user.

My question is, after I test all these with my FakeUserRepository implementation, I have to go back and implement the IUserRepository on my actual persistence dataset (i.g. SQL), and I have to implement again the code, so my unit testing is not actually checking the code that I am actually using on my application.

Maybe I am missing something.

Thanks as always!

Below then my Persistent data access repository which is the one that is supposed to be under test (by my mind at least), but then I should not test hooked to the database:

public class SQLUserRepository : IUserRepository
{
    private BusinessDomainModel.EntityModel.BusinessHelperAccountDBEntities _repository;

    public SQLUserRepository()
    {
        _repository = new BusinessHelperAccountDBEntities();
    }

    #region IUserRepository Members

    public IQueryable<User> FindAllUsers()
    {
        return _repository.UserSet.AsQueryable();
    }
+3  A: 

You should test the real class not a fake one you make for testing. The point of using an interface is that it allows you to mock out the class, so you can use the mock version in tests with other collaborators.

In order to test the class you should be able to pass in a mock database and assert that the calls you expected to be made in to the database actually happen when you call the methods on your Repository class.

Here is a good intro to mocking and testing in C#:

http://refact.blogspot.com/2007/01/mock-objects-and-rhino.html

photographique
+5  A: 

Never test a mock. The class under test should always be a real instance of the class, though you can and should mock any of its dependencies so you can test it in isolation.

tvanfosson
Hi tvanfosson: Thanks for the reply. I understand your post, and that's the reason I have the question. If I should not test my FakeImplementation of the IUserRepository, then I have to test the SQLUserRepository implementation of IUserRepository, which is the one that I am using. But If I do that then I am testing with Database and I have read that this should be avoided. I know I am missing something to make the connection, I just don't know what it is. Thanks.
Geo
@Geo: By testing the actual SqlRepository you are already in Integration-Test land.. For a unit test not testing the SqlRepository at all is totally valid.
Tigraine
I cheat. My repositories are actually backended by a LINQ data context. I mock out the LINQ data context when testing the repository. I don't bother unit testing the data context because it is generated code. You will need some integration tests, though, if you go this route -- you would anyway, right? -- because LINQ to SQL behaves differently than LINQ to Objects (the mock version).
tvanfosson
+2  A: 

Hi Geo,

I'll explain what I'm doing, why and how much milage I get out it.

First, I'm doing exactly what you are doing, regarding your repositories. Despite some namespace differences, this is what I also do:

  • MyProject.Repositories.IUserRepository
  • MyProject.Repositories.Fake.UserRepository
  • MyProject.Repositories.SqlServer.UserRepository

With my fake UserRepository, I also just create and populate a private IEnumerable<User> collection (which is a List<User>). Why do I have this? I use this repository for my initial day to day development (because it's fast -> no db access == quick!). Then i swap over the fake respitories for the sql repositories (ie chage my dependency injection (oooohhh!)). This is why this class/namespace exists, as opposed to using Mocks in my unit test for 'fake' stuff. (That happens, but under different circumstances).

With my sql server UserRepository, I use LinqToSql. With regards to you question, it's irrelivant that I'm using LinqToSql ... it could be any other database wrapper. The important thing here is that there's a 3rd party something which i'm integrating with.


Ok, so from here, I need to make sure of two things

  1. The fake UserRepostiory works
  2. The sql server UserRepository works.

First up, most people don't create a unit test for a fake thing. It's a fake piece of turd, so why waste the energy? True --- except that I use that fake piece of turd in my day to day development (refer to my blarg about this, above). So i quickly whip up a few basic unit tests. NOTE: In my eyes these are unit tests, even though they are repository classes. Why? They aren't intergrating with a 3rd party/infrastructure.

Next (finally I get to the point), I do a seperate test class which is an Intergration Test. This is a unit test that will intergrate with something outside of the system. It could be the real Twitter api. It could be the real S3 Amazon api. Notice i used the word real. That's the key, here. I'm intergrating with a real service OUTSIDE of mine. As such -> it's slow. Anytime i need to leave my computer for some data, it's called intergrating and you automatically assume (and expect) it to be slow.

So here, i'm intergrating with a Database.

(Nae sayers, please don't Troll this with cheeky suggestions that you have the database on the same computer ... you're leaving your APPLICATION 'world').

Wow. this is some War-n-Peace novel .. time for some hard action, cock slappin code. Lets bring it!

namespace MyProject.Tests.Repositories.SqlServer
{
    // ReSharper disable InconsistentNaming

    [TestClass]
    public class UserRepositoryTests : TestBase
    {
        [ClassInitialize]
        public static void ClassInitialize(TestContext testContext)
        {
            // Arrange.
            // NOTE: this method is inherited from the TestBase abstract class.
            // Eg. protected IUserRepository = 
            //     new MyProject.Respositories.SqlServer
            //         .UserRespository(connectionString);
            InitializeSqlServerTestData();
        }

        [TestMethod]
        public void GetFirst20UsersSuccess()
        {
            // Act.
            var users = _users.GetUsers()
                .Take(20)
                .ToList();

            // Assert.
            Assert.IsNotNull(users);
            Assert.IsTrue(users.Count() > 0);
        }
    }
}

Ok, lets run through this puppy.

First up, this is using Microsoft Unit Testing - built into VS2010 Beta2 or with the Team Foundation edition of VS2008 (or whatever that version is ... i just install the copy our work has purchased).

Second, whenever the class is first initialized (be it one test or many), it creates the context. In my case, my Sql Server UserRepository which will use a LinqToSql context. (Yours will be an EF context). This is the Arrange part of TDD.

Third, i call the method -> this is the Act part of TDD.

Last, I check if i got back what i expected -> this is the Assert part of TDD.


What about updating the DB?

Just follow the same pattern except you might want to wrap your calling code in a transaction and the roll it back. Otherwise u might get 100's of rows of data which could possibly be the same. Downside to this? Any identity fields will not have all nice and pretty numbering sequence (becuase the rollback will 'use' that number). Doesn't make sence? don't worry. that's an advanced tip i thought i'd throw in to test you out, but it means diddly squat for this hellishly long post.


so .. er.. yeah. that's what i do. Don't know if the Gods of Programming, on these forums, will flip and throw mud my way but I sorta like it and I'm hoping it might help ya.

HTH.

Pure.Krome
Love the post thanks.
David
:) wikid :) glad this helps at least one person :)
Pure.Krome