How are people unit testing with Entity Framework 6, should you bother?
How are people unit testing with Entity Framework 6, should you bother?
I am just starting out with Unit testings and TDD in general. I have dabbled before but now I am determined to add it to my workflow and write better software.
I asked a question yesterday that kind of included this, but it seems to be a question on its own. I have sat down to start implementing a service class that I will use to abstract away the business logic from the controllers and map to specific models and data interactions using EF6.
The issue is I have roadblocked myself already because I didn't want to abstract EF away in a repository (it will still be available outside the services for specific queries, etc) and would like to test my services (EF Context will be used).
Here I guess is the question, is there a point to doing this? If so, how are people doing it in the wild in light of the leaky abstractions caused by IQueryable and the many great posts by Ladislav Mrnka on the subject of unit testing not being straightforward because of the differences in Linq providers when working with an in memory implementation as apposed to a specific database.
The code I want to test seems pretty simple. (this is just dummy code to try and understand what i am doing, I want to drive the creation using TDD)
Context
public interface IContext { IDbSet Products { get; set; } IDbSet Categories { get; set; } int SaveChanges(); } public class DataContext : DbContext, IContext { public IDbSet Products { get; set; } public IDbSet Categories { get; set; } public DataContext(string connectionString) : base(connectionString) { } }
Service
public class ProductService : IProductService { private IContext _context; public ProductService(IContext dbContext) { _context = dbContext; } public IEnumerable GetAll() { var query = from p in _context.Products select p; return query; } }
Currently I am in the mindset of doing a few things:
- Mocking EF Context with something like this approach- Mocking EF When Unit Testing or directly using a mocking framework on the interface like moq - taking the pain that the unit tests may pass but not necessarily work end to end and back them up with Integration tests?
- Maybe using something like Effort to mock EF - I have never used it and not sure if anyone else is using it in the wild?
- Not bother testing anything that simply calls back to EF - so essentially service methods that call EF directly (getAll etc) are not unit tested but just integration tested?
Anyone out there actually doing this out there without a Repo and having success?
Answer by Jonathan Henson for How are people unit testing with Entity Framework 6, should you bother?
I would not unit test code I don't own. What are you testing here, that the MSFT compiler works?
That said, to make this code testable, you almost HAVE to make your data access layer separate from your business logic code. What I do is take all of my EF stuff and put it in a (or multiple) DAO or DAL class which also has a corresponding interface. Then I write my service which will have the DAO or DAL object injected in as a dependency (constructor injection preferably) referenced as the interface. Now the part that needs to be tested (your code) can easily be tested by mocking out the DAO interface and injecting that into your service instance inside your unit test.
//this is testable just inject a mock of IProductDAO during unit testing public class ProductService : IProductService { private IProductDAO _productDAO; public ProductService(IProductDAO productDAO) { _productDAO = productDAO; } public List GetAllProducts() { return _productDAO.GetAll(); } ... }
I would consider live Data Access Layers to be part of integration testing, not unit testing. I have seen guys run verifications on how many trips to the database hibernate makes before, but they were on a project that involved billions of records in their datastore and those extra trips really mattered.
Answer by Justin for How are people unit testing with Entity Framework 6, should you bother?
If you want to unit test code then you need to isolate your code you want to test (in this case your service) from external resources (e.g. databases). You could probably do this with some sort of in-memory EF provider, however a much more common way is to abstract away your EF implementation e.g. with some sort of repository pattern. Without this isolation any tests you write will be integration tests, not unit tests.
As for testing EF code - I write automated integration tests for my repositories that write various rows to the database during their initialization, and then call my repository implementations to make sure that they behave as expected (e.g. making sure that results are filtered correctly, or that they are sorted in the correct order).
These are integration tests not unit tests, as the tests rely on having a database connection present, and that the target database already has the latest up-to-date schema installed.
Answer by Liath for How are people unit testing with Entity Framework 6, should you bother?
This is a topic I'm very interested in. There are many purists who say that you shouldn't test technologies such as EF and NHibernate. They are right, they're already very stringently tested and as a previous answer stated it's often pointless to spend vast amounts of time testing what you don't own.
However, you do own the database underneath! This is where this approach in my opinion breaks down, you don't need to test that EF/NH are doing their jobs correctly. You need to test that your mappings/implementations are working with your database. In my opinion this is one of the most important parts of a system you can test.
Strictly speaking however we're moving out of the domain of unit testing and into integration testing but the principals remain the same.
The first thing you need to do is to be able to mock your DAL so your BLL can be tested independently of EF and SQL. These are your unit tests. Next you need to design your Integration Tests to prove your DAL, in my opinion these are every bit as important.
There are a couple of things to consider:
- Your database needs to be in a known state with each test. Most systems use either a backup or create scripts for this.
- Each test must be repeatable
- Each test must be atomic
There are two main approaches to setting up your database, the first is to run a UnitTest create DB script. This ensures that your unit test database will always be in the same state at the beginning of each test (you may either reset this or run each test in a transaction to ensure this).
Your other option is what I do, run specific setups for each individual test. I believe this is the best approach for two main reasons:
- Your database is simpler, you don't need an entire schema for each test
- Each test is safer, if you change one value in your create script it doesn't invalidate dozens of other tests.
Unfortunately your compromise here is speed. It takes time to run all these tests, to run all these setup/tear down scripts.
One final point, it can be very hard work to write such a large amount of SQL to test your ORM. This is where I take a very nasty approach (the purists here will disagree with me). I use my ORM to create my test! Rather than having a separate script for every DAL test in my system I have a test setup phase which creates the objects, attaches them to the context and saves them. I then run my test.
This is far from the ideal solution however in practice I find it's a LOT easier to manage (especially when you have several thousand tests), otherwise you're creating massive numbers of scripts. Practicality over purity.
I will no doubt look back at this answer in a few years (months/days) and disagree with myself as my approaches have changed - however this is my current approach.
To try and sum up everything I've said above this is my typical DB integration test:
[Test] public void LoadUser() { this.RunTest(session => // the NH/EF session to attach the objects to { var user = new UserAccount("Mr", "Joe", "Bloggs"); session.Save(user); return user.UserID; }, id => // the ID of the entity we need to load { var user = LoadMyUser(id); // load the entity Assert.AreEqual("Mr", user.Title); // test your properties Assert.AreEqual("Joe", user.Firstname); Assert.AreEqual("Bloggs", user.Lastname); } }
The key thing to notice here is that the sessions of the two loops are completely independent. In your implementation of RunTest you must ensure that the context is committed and destroyed and your data can only come from your database for the second part.
Edit 13/10/2014
I did say that I'd probably revise this model over the upcoming months. While I largely stand by the approach I advocated above I've updated my testing mechanism slightly. I now tend to create the entities in in the TestSetup and TestTearDown.
[SetUp] public void Setup() { this.SetupTest(session => // the NH/EF session to attach the objects to { var user = new UserAccount("Mr", "Joe", "Bloggs"); session.Save(user); this.UserID = user.UserID; }); } [TearDown] public void TearDown() { this.TearDownDatabase(); }
Then test each property individually
[Test] public void TestTitle() { var user = LoadMyUser(this.UserID); // load the entity Assert.AreEqual("Mr", user.Title); } [Test] public void TestFirstname() { var user = LoadMyUser(this.UserID); Assert.AreEqual("Joe", user.Firstname); } [Test] public void TestLastname() { var user = LoadMyUser(this.UserID); Assert.AreEqual("Bloggs", user.Lastname); }
There are several reasons for this approach:
- There are no additional database calls (one setup, one teardown)
- The tests are far more granular, each test verifies one property
- Setup/TearDown logic is removed from the Test methods themselves
I feel this makes the test class simpler and the tests more granular (single asserts are good)
Edit 5/3/2015
Another revision on this approach. While class level setups are very helpful for tests such as loading properties they are less useful where the different setups are required. In this case setting up a new class for each case is overkill.
To help with this I now tend to have two base classes SetupPerTest
and SingleSetup
. These two classes expose the framework as required.
In the SingleSetup
we have a very similar mechanism as described in my first edit. An example would be
public TestProperties : SingleSetup { public int UserID {get;set;} public override DoSetup(ISession session) { var user = new User("Joe", "Bloggs"); session.Save(user); this.UserID = user.UserID; } [Test] public void TestLastname() { var user = LoadMyUser(this.UserID); // load the entity Assert.AreEqual("Bloggs", user.Lastname); } [Test] public void TestFirstname() { var user = LoadMyUser(this.UserID); Assert.AreEqual("Joe", user.Firstname); } }
However references which ensure that only the correct entites are loaded may use a SetupPerTest approach
public TestProperties : SetupPerTest { [Test] public void EnsureCorrectReferenceIsLoaded() { int friendID = 0; this.RunTest(session => { var user = CreateUserWithFriend(); session.Save(user); friendID = user.Friends.Single().FriendID; } () => { var user = GetUser(); Assert.AreEqual(friendID, user.Friends.Single().FriendID); }); } [Test] public void EnsureOnlyCorrectFriendsAreLoaded() { int userID = 0; this.RunTest(session => { var user = CreateUserWithFriends(2); var user2 = CreateUserWithFriends(5); session.Save(user); session.Save(user2); userID = user.UserID; } () => { var user = GetUser(userID); Assert.AreEqual(2, user.Friends.Count()); }); } }
In summary both approaches work depending on what you are trying to test.
Answer by Grax for How are people unit testing with Entity Framework 6, should you bother?
I like to separate my filters from other portions of the code and test those as I outline on my blog here http://coding.grax.com/2013/08/testing-custom-linq-filter-operators.html
That being said, the filter logic being tested is not identical to the filter logic executed when the program is run due to the translation between the LINQ expression and the underlying query language, such as T-SQL. Still, this allows me to validate the logic of the filter. I don't worry too much about the translations that happen and things such as case-sensitivity and null-handling until I test the integration between the layers.
Answer by samy for How are people unit testing with Entity Framework 6, should you bother?
Effort Experience Feedback here
After a lot of reading I have been using Effort in my tests: during the tests the Context is built by a factory that returns a in memory version, which lets me test against a blank slate each time. Outside of the tests, the factory is resolved to one that returns the whole Context.
However i have a feeling that testing against a full featured mock of the database tends to drag the tests down; you realize you have to take care of setting up a whole bunch of dependencies in order to test one part of the system. You also tend to drift towards organizing together tests that may not be related, just because there is only one huge object that handles everything. If you don't pay attention, you may find yourself doing integration testing instead of unit testing
I would have prefered testing against something more abstract rather than a huge DBContext but i couldn't find the sweet spot between meaningful tests and bare-bone tests. Chalk it up to my inexperience.
So i find Effort interesting; if you need to hit the ground running it is a good tool to quickly get started and get results. However i think that something a bit more elegant and abstract should be the next step and that is what I am going to investigate next. Favoriting this post to see where it goes next :)
Edit to add: Effort do take some time to warm up, so you're looking at approx. 5 seconds at test start up. This may be a problem for you if you need your test suite to be very efficient.
Edited for clarification:
I used Effort to test a webservice app. Each message M that enters is routed to a IHandlerOf
via Windsor. Castle.Windsor resolves the IHandlerOf
which resovles the dependencies of the component. One of these dependencies is the DataContextFactory
, which lets the handler ask for the factory
In my tests I instantiate the IHandlerOf component directly, mock all the sub-components of the SUT and handles the Effort-wrapped DataContextFactory
to the handler.
It means that I don't unit test in a strict sense, since the DB is hit by my tests. However as I said above it let me hit the ground running and I could quickly test some points in the application
Answer by user2992192 for How are people unit testing with Entity Framework 6, should you bother?
I have fumbled around sometime to reach these considerations:
1- If my application access the database, why the test should not? What if there is something wrong with data access? The tests must know it beforehand and alert myself about the problem.
2- The Repository Pattern is somewhat hard and time consuming.
So I came up with this approach, that I don't think is the best, but fulfilled my expectations:
Use TransactionScope in the tests methods to avoid changes in the database.
To do it it's necessary:
1- Install the EntityFramework into the Test Project. 2- Put the connection string into the app.config file of Test Project. 3- Reference the dll System.Transactions in Test Project.
The unique side effect is that identity seed will increment when trying to insert, even when the transaction is aborted. But since the tests are made against a development database, this should be no problem.
Sample code:
[TestClass] public class NameValueTest { [TestMethod] public void Edit() { NameValueController controller = new NameValueController(); using(var ts = new TransactionScope()) { Assert.IsNotNull(controller.Edit(new Models.NameValue() { NameValueId = 1, name1 = "1", name2 = "2", name3 = "3", name4 = "4" })); //no complete, automatically abort //ts.Complete(); } } [TestMethod] public void Create() { NameValueController controller = new NameValueController(); using (var ts = new TransactionScope()) { Assert.IsNotNull(controller.Create(new Models.NameValue() { name1 = "1", name2 = "2", name3 = "3", name4 = "4" })); //no complete, automatically abort //ts.Complete(); } } }
Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72
0 comments:
Post a Comment