4

I'm looking to unit testing some methods using Entity Framework 6 using the instructions provided here.

My set-up is slightly different though - I'm also using ASP.Net Identity (the default implementation that uses EF). As such my context inherits from IdentityDbContext.

When I run the tests I get an exception with the following details:

Castle.Proxies.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.    
Castle.Proxies.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.    
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.    
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.

I've read that in normal uses these are set up in the default OnModelCreating method.

But can anyone offer advice on handling this within the mocked context illustrated in the method linked to above?

Thanks

Andy

Andy Butland
  • 438
  • 4
  • 17

1 Answers1

0

As pointed out in that article, when you mock out EF types you completely change their behaviour: the queries get executed as LINQ-to-Objects, not LINQ-to-SQL. This means you can easily write unit tests that pass for code that fails when it is used in the real world. A good example is:

context.Foos.Where(candidate => SomePredicate(candidate));

That means you need to fully understand valid LINQ-to-SQL expressions to write the unit tests, but you were writing those unit tests because you wanted to validate your understanding. So what has that really bought you? How much value have you got from those unit tests? I'm saying this from the perspective of someone who once spent ages mocking out my EF layer only to find that once the database got involved the code was wrong.

A better testing strategy in my opinion is to create Gateway classes that act as an intermediary between your DbContext and your consumers of that context.

class FooGateway : IFooGateway
{
    IEnumerable<Foo> GetFoosThatObeySomePredicate() { /* ... */ }
}

The Gateways expose interfaces that can be effectively mocked. Use those interfaces in your data consuming classes so that you can write high quality unit tests and then accept that you need to write integration tests to test the Gateway classes themselves: and those tests need a real database, so you need infrastructure for creating databases as part of your testing.

satnhak
  • 9,407
  • 5
  • 63
  • 81
  • Thanks for the comments. And yes, you point out an important caveat with this process. Seems to me though there's still some value in doing this. You're right that a passing test could fail once the database is brought in, but a failing test wouldn't unexpectedly pass - so there's some benefit from a regression testing point of view. So point taken, but would still be interested in trying to get it to work. – Andy Butland May 02 '14 at 21:15
  • But the point is you are better spent investing that time in putting together a decent integration testing infrastructure. – satnhak May 03 '14 at 09:12