1

I have a project based on EF Code First using MSSQL server. In one of my repositories I'm dynamically building a query using PredicateBuilder. To test all possible outcome manually every time something changes in the code, is very time consuming.

For that reason, I'd like to automate this by unit testing it. I was thinking about using sql compact for unit test and MSSQL server for production. But how would one enable migrations for sql compact server?

This is dbContext class:

public partial class ApplicationDbContext :
        IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext() : base("name=DefaultConnection") { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
        base.OnModelCreating(modelBuilder);
     }
}

Repository:

public class FilterRepository : IFilterRepository
{
    private ApplicationDbContext _dbContext;

    public FilterRepository(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
        DbInterception.Add(new CommandInterceptor());
    }

    public IEnumerable<Person> GetPersons(Filter filter)
    {
        try
        {
            var persons = PredicateBuilder.False<Person>();

            _dbContext.Configuration.AutoDetectChangesEnabled = false;

            var result = _dbContext.Persons
                            .AsNoTracking() 
                            .Where(persons)
                            .OrderBy(x => x.Name)
                            .Skip(filter.Skip)
                            .Take(10)
                            .ToList();

            _dbContext.Configuration.AutoDetectChangesEnabled = true;

            return result;
        }
        catch (Exception ex)
        {

        }
    }
}
Quoter
  • 4,236
  • 13
  • 47
  • 69
  • See http://stackoverflow.com/questions/9907905/entity-framework-code-first-how-to-seed-a-database-for-unit-testing. In short - create an initializer that drops and recreates your compact database, and then seed it. Initialize the context at the start of your unit test, and it will be recreated each time the tests are run. However, if you are wanting to hit a database, this is more of an integration test than a unit test. – Brendan Green Feb 11 '15 at 22:52

1 Answers1

2

ok, so switching to a different database for testing is (imo) a bad idea.

Here is some terminology for you, that might help you to organize your tests better.

A Unit test is for testing your business functionality, generally you would just mock your data for this, as you want to test what happens to your inputs (data) within your domain classes, not where they came from.

An Integration test is for testing how your business domain layer interacts with your service layer (your data layer is a service layer), I would also regard testing the correctness of a query as an integration test for purposes of running them.

So in your case, don't confuse things and potentially add unexpected behavior caused by differences in how the 2 databases work. If you are testing your business functionality mock your data for that. Its faster for a start, and you will be testing, just what you want to be testing.

With your integration testing, you are testing that your interaction with the service layer is correct, in this case your test is not really testing that the business logic works, that should be covered in the unit tests; Test that your queries return the correct data for its predicates, and that any data that is to be persisted is persisted correctly. Also that any transactions in play are working as you expect. End to end testing of a scenario is also a valid integration test.

You absolutely need to be doing this on the same database platform as your production, don't expect SQL Compact and SQL Server to behave the same way.

Edit...For the comment.

So the normal way that you would Mock your repository call is to use dependency injection, you don't have to do it this way, but its easier and pretty much best practice.

The idea is that in your domain class that uses your data, you would first get your repository, or query class from the DI container that was either injected into the constructor or pulled from the DI container

// in your domain class you would have something like...
    var repo = container.Get<IRepository>();
    var myList = repo.GetMyObjects(predcate);

So with Moq your could now simply mock that call

//Where you do your container registration..
var repo = Mock<IRepository>
repo.Setup( o => o.GetMyObject(predecate)).Returns( (predecate) => <your dummy list>));
container.Register(repo.Object);
// Then later on your business domain object gets the dummy repo instead.

Note this is pseudo code, will be slightly different per DI and Mocking frameworks used.

Tim Jarvis
  • 18,465
  • 9
  • 55
  • 92
  • If I mock the data, how can I pass the mocked data to my repository? See my updated answer in a minute or 1 for the repository code. – Quoter Feb 11 '15 at 22:59
  • Your mocked repository will return the data that you set up for the given test. See something like: https://github.com/Moq/moq4/wiki/Quickstart. That is, you configure the mock so that a call to `GetPersons` will operate against your test data. – Brendan Green Feb 11 '15 at 23:10
  • @Tim Jarvis, I'm manly stuck with creating the database in memory, and I came across this blog post: https://msdn.microsoft.com/en-US/data/dn314431#doubles. They created an interface for their dbcontext to fill up the in-memory database. Is this a good post to use it as a reference? – Quoter Feb 12 '15 at 18:30
  • @Quoter Well to be honest if you find yourself trying to put your dbContext in memory, I'd question your test. I honestly think its a bad idea...in fact in this case you would be executing your Linq against Linq to objects and not Linq to EF - they behave differently - as far as I am concerned this makes the test potentially invalid. – Tim Jarvis Feb 13 '15 at 00:39
  • @Quoter having said all that my understanding is that EF 7.0 will provide support for In Memory data. - I think you can download the beta. So if you really insist on doing this...its probably possible with the next version. – Tim Jarvis Feb 13 '15 at 00:50
  • @Tim Javis, how do people unit test their database access logic? And did you see the link I pasted? Otherwise a blogpost would be really helpful. Words in a comment alone won't help me understand how to test the database access logic, like filtering data. – Quoter Feb 13 '15 at 10:08
  • @Quoter As a mentioned in my answer, people test their db access logic using Integration tests. (Unit tests over the service layer). There is no trick to it. They are just slower than Unit Tests. My point was simply don't substitute your database in an Integration test, because its not a valid test, the different db will behave differently, sometimes very subtly. – Tim Jarvis Feb 14 '15 at 00:59