views:

204

answers:

5

Given a basic repository interface:

public interface IPersonRepository
{
    void AddPerson(Person person);
    List<Person> GetAllPeople();
}

With a basic implementation:

public class PersonRepository: IPersonRepository
{
    public void AddPerson(Person person) 
    {
        ObjectContext.AddObject(person);
    }

    public List<Person> GetAllPeople()
    {
        return ObjectSet.AsQueryable().ToList();
    }
}

How can you unit test this in a meaningful way? Since it crosses the boundary and physically updates and reads from the database, thats not a unit test, its an integration test.

Or is it wrong to want to unit test this in the first place? Should I only have integration tests on the repository?

I've been googling the subject and blogs often say to make a stub that implements the IRepository:

public class PersonRepositoryTestStub: IPersonRepository
{
    private List<Person> people = new List<Person>();
    public void AddPerson(Person person) 
    {
        people.Add(person);
    }

    public List<Person> GetAllPeople()
    {
        return people;
    }
}

But that doesnt unit test PersonRepository, it tests the implementation of PersonRepositoryTestStub (not very helpful).

+5  A: 

In this specific case I think you don't need to make a unit test for your repository class, as the implementation is simply invoking the ObjectContext object, so it will be like testing something you didn't build (which is not the idea). In case you don't have any complex logic, my recommendation is not to waste time on making a unit test for that class. What you say about the PersonRepositoryTestStub is a fake implementation of the repository for testing the layer that is above of your DAL.

uvita
Yeah, thats what I was leaning towards. My next question would have been about the next layer up (IService in this case). If repository is not worth testing and PersonService.GetAllPeople() simply says "return _repository.GetAllPeople();", then is there any point in testing that either?
JK
@JK There's not really any benefit to testing that. If a class/method just provides glue between other classes I don't usually test it individually. I just wire everything together and run a smoke/integration test of a few methods. eg. Can I create a new object, save it, and retrieve it?
Ryan
You could test that your repository gets called. I agree that this is not a meaningful test, I would focus on testing those methods that are more critic for your business.
uvita
testing the one line method which are calling the context.. I don't see the point. Not everything has to be tested. test what needs to be tested (that's already a LOT of tests...)
Stephane
I agree with Ryan. If you want to test this stuff, set up an Integration test (which you can still use your favourite unit testing framework for if you like) that tests that the configuration of the system works. I find that having these sorts of tests is very helpful for finding silly configuration mistakes that would only be exposed at runtime.
Jamie Penney
A: 

The time when I think this type of testing makes sense is when your repository interface is generic. E.g.

public interface IEntity
{
    int Id { get; set; }
}

public interface IRepository<TEntity>
    where TEntity : IEntity
{
    void Add(TEntity entity);
    List<TEntity> GetAll();
}

If you have multiple implementations of that interface, then it is also possible to write a generic test fixture that tests against that interface, allowing you to test multiple repository implementations with a single test fixture. This approach does not differentiate between unit and integration tests, because it has no idea what the implementation is.

Let me know if this type of thing interests you and I can post a complete example.

jimr
A: 

I would test Business Logic layer that depends on DAL implementation directly (has strong reference to DAL exact implementation) or indirectly (abstracted from DAL via interfaces).

I am not very fond of tests, that uses stub implementation, just wrap database calls into uncomitted transaction (that rolls back all data changes when test finishes even if exception is thrown).

Andrew Florko
+2  A: 

I have run into the same problem. I wrote a slew of unit tests against an implementation of my repository interface that was a fake repository. Shortly after completing it, I realized that I wrote the unit test to test the fake repository and I wrote the fake repository simply to support the unit tests. This seemed like a large amount of useless code.

I have come to the conclusion that I don't need the unit tests but that the fake repository implementation is good because I can use it as the repository my services (and therefore my controllers) use so that unit tests against those will be predictable (thanks to a predefined fake repository).

So, I have decided to leave the unit tests against the fake repository in there as they are helpful to test my fake repository so that I can be assured that the higher levels of my application are using fully tested fakes.

In other words, the fake repository and the unit tests against the fake repository are the "supporting cast" for higher levels of the applications and the unit tests against higher levels of the application.

Hope that helps.

Brian McCord
A: 

I need to write unit tests against this interface:

public interface IRepository where TEntity : class { /// /// /// /// IQueryable GetQuery();

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    IEnumerable<TEntity> GetAll();

    /// <summary>
    /// 
    /// </summary>
    /// <param name="where"></param>
    /// <returns></returns>
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> where);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="where"></param>
    /// <returns></returns>
    TEntity Single(Expression<Func<TEntity, bool>> where);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="where"></param>
    /// <returns></returns>
    TEntity First(Expression<Func<TEntity, bool>> where);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    void Delete(TEntity entity);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    void Add(TEntity entity);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="entity"></param>
    void Attach(TEntity entity);

    /// <summary>
    /// 
    /// </summary>
    void SaveChanges();
}

Has anyone done something like this?

ryan martin