views:

91

answers:

5

I'm writing NUnit tests for a service.

I've decided to do this by sending direct SQL statements to the database instead of using the Entity Framework model so that I could be certain of the results, i.e. I am testing what is in the database and not what the Entity Framework tells me is in the database (e.g. it could be reporting a cached result, etc.)

The only drawback is that it is getting tedious to write this code using SqlCommand, SqlDataReader, etc. and being able to use the EF model would be much easier.

How are others doing this? Is it good practice to use Entity Framework when writing tests or should direct calls to the database be used to ensure accurate results?

+1  A: 

I would say it is a bad practice when testing the service cause it creates a dependency on the database.

What I did, was roll up my Model classes into a Repository pattern. The repository pattern was coded to an interface so that I could change the implementation of the database querying without effecting other parts of the code. It could be easily mocked and used for testing. This is the interface I constructed.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace App.Core.Repositories
{
    public interface IRepository<TKey, TModel>
        where TKey : IComparable
        where TModel : class
    {
        TModel Only(TKey key);
        IQueryable<TModel> Where(Func<TModel, bool> query);

        TModel Single(Func<TModel, bool> query);
        TModel First(Func<TModel, bool> query);
        IQueryable<TModel> All();

        void Insert(TModel entity);
        void Insert(IEnumerable<TModel> entities);

        void Update(TModel entity);
        void Update(IEnumerable<TModel> entities);

        void Remove(TModel entities);
        void Remove(Func<TModel, bool> query);
        void Remove(IEnumerable<TModel> entities);
    }

    public interface IRepository<TModel> : IRepository<int, TModel> where TModel : class { }

    public interface IRepository : IRepository<int, object> { }
}
Daniel A. White
We use Repositories as well, but we still *also* unit test the database library that implements the Repositories. They are two different things.
Mark Seemann
A: 

Instead of using LINQ to Entities, we use LINQ to SQL for Back Door Manipulation in our unit tests, as it's a little closer to the database schema.

However, instead of getting tired of doing all the SqlCommand, etc. stuff, we get tired of keeping the auto-generated L2S code in sync with the actual database...

Mark Seemann
Wouldn't it be more than sufficient to use an EF context in another appdomain to ensure that you are seeing persisted data? Especially since L2SQL ties you to MSSQL.
Robert Giesecke
Point taken on being tied to MSSQL. However, do you think that spinning up EF in another AppDomain is necessary? I would think that a different EF context would be enought. It would hardly be Back Door Manipulation then, so at that point I think that the exercise would be redundant.
Mark Seemann
+1  A: 

Quite the opposite. The tests for our DA layer are not concerned about the database at all.

For example. We have a test that calls the Save method to save a new data object. It then calls the Load method to check that the object can be loaded back. I don't care weather that object was actually saved to the database or not, all I care about is that I can retrieve objects that were saved. If the EF wants to do some clever caching and not save immediately then that's fine by me.

Consider that in the future we might not use a SQL server back end any more, we might switch to oracle, or even a NOSQL solution. I don't want all my DA layer unit tests to break when I switch the back end. It's actually the reverse, I need my DA layer unit tests to continue to work unchanged so I can validate that the switch of the storage solution doesn't effect the semantics or use of the DA layer.

Simon P Stevens
-1 Even when you have a clear separation between abstract Repositories and their implementation, you may still want to test the actual implementation. I don't disagree with this separation, but find it a logical fallacy to say that thereby follows that testing the DAL is bad practice.
Mark Seemann
@Mark Seemann: I didn't say testing the DAL was bad practise. I said test the DAL for what it is intended to do (store and retrieve stuff), don't tightly couple your tests to your current specific back-end storage of the DAL by including storage specific code in your DAL tests.
Simon P Stevens
+1  A: 

Well, let's put it this way: You shouldn't be writing unit tests to ensure that the Entity Framework itself works. That's Microsoft's job.

Also, some would argue that getting a SQL Server database in a "unit test" at all means that you are now doing integration testing rather than unit testing. This, regardless of how you do it. I'm not going to argue that point, though.

The unit tests that you write should be testing your own code.

You say that one of the reasons you want to read the database directly is that your application could be seeing cached data. Who wrote that cache? If it's you, then by all means unit test it, although I would probably mock out the database access in this case.

But if it's SQL Server's (or the Entity Framework's) caching you're concerned about, well, where would you begin? These products are huge, and there is no way that you're going to unit test the entire system.

This does not mean that you should presume that SQL Server and the Entity Framework are defect-free. But you typically catch errors in your framework via integration testing your application, rather than unit testing.

Craig Stuntz
A: 

As Craig and others already pointed out: You are doing Integration Testing.

Which means that I have to answer your main question with "No".

However, for your integrated testing, you might be doing more than fine to bypass any in-memory caching by using an EF context that is located in another appdomain. Every appdomain sees its own set of static values, so even if EF were to use a static cache in version 5(which I doubt), you could test it in a db-agnostic way. (repeating those tests for all backends your software supports.)

This is the paranoid version, btw. A second context should already do the trick.

Robert Giesecke