6

I've been trying to find a good solution to this but with no luck, so either I'm not searching for the right keywords, or we're doing things wrong from the start so the problem shouldn't really exist.

Update for clarification: I would like this to work as a unit test rather than as an integration test, so I don't want this to hit the database, but I want to mock the associations made when EF persists changes in my unit test.

Original question:

Say you are testing a service method like so:

[Test]
public void AssignAuthorToBook_NewBookNewAuthor_SuccessfullyAssigned()
{
  IBookService service = new BookService();

  var book = new Book();

  var Author = new Author() { Id = 123 };

  service.AssignAuthorToBook(book, author);

  Assert.AreEqual(book.AuthorId, 123);
}

Now lets say that this test fails because AssignAuthorToBook actually works using the code book.Author = author; so it is not assigning the AuthorId, it is assigning the entity. When this is persisted using the Entity Framework SaveChanges() method on the context it will associate the entities and the IDs will correlate. However, in my example above the logic of Entity Framework would not have been applied. What I am saying is the code will work once SaveChanges() has been called, but the unit test will fail.

In this simple example, you'd probably know straight away why your test had failed as you had just written the test immediately before the code and could easily fix it. However, for more complicated operations and for operations where future changes that may change the way entities are associated, which will break tests but may not break functionality, how is unit testing best approached?

My thoughts are:

  • The service layer should be ignorant of the persistence layer - should we mock the Data Context in the unit tests to mock the manner that it works? Is there an easy way to do this that will automatically tie up the associations (i.e. assign to correct entity if Id is used or assign the correct Id if the entity is used)?
  • Or should the tests be structured in a slightly different manner?

The tests that exist in the current project I have inherited work as in my example above, but it niggles with me that there is something wrong with the approach and that I haven't managed to find a simple solution to a possibly common problem. I believe the Data Context should be mocked, but this seems like a lot of code will need to be added to the mock to dynamically create the associations - surely this has already been solved?

Update: These are the closest answers I've found so far, but they're not quite what I'm after. I don't want to test EF as such, I just wondered what was best practise for testing service methods that access repositories (either directly or via navigation properties through other repositories sharing the same context).

How Do I Mock Entity Framework's Navigational Property Intelligence?

Mocked datacontext and foreign keys/navigation properties

Fake DbContext of Entity Framework 4.1 to Test

Navigation properties not set when using ADO.NET Mocking Context Generator

Conclusion so far: That this is not possible using unit testing, and only possible using integration testing with a real DB. You can get close and probably code something to dynamically associate the navigation properties, but your mock data context will never quite replicate the real context. I would be happy if any solution enabled me to automatically associate the navigation properties which would allow my unit tests to be better, if not perfect (the nature of a successful unit test doesn't by any means guarantee functionality anyway) The ADO.NET Mocking Context Generator comes close, but it appears that I'll have to have a mock version of every entity which will not work for me in case functioanlity is added to them using partial classes in my implementation.

Community
  • 1
  • 1
SilverlightFox
  • 32,436
  • 11
  • 76
  • 145
  • This might be helpful: http://romiller.com/2012/02/14/testing-with-a-fake-dbcontext/ – Pawel Aug 15 '13 at 05:16
  • 1
    @Pawel Thanks, but I can't see how the fake DbContext mocks the associations as `SaveChanges` does nothing. – SilverlightFox Aug 15 '13 at 09:50
  • 1
    Unit testing scenarios where a key piece of the puzzle is implemented by an ORM is, IMO, a pointless exercise in frustration. Swap out the database to a lightweight db (eg. SQLite or SQL CE) for the tests and be done with it. :) – Rytmis Aug 16 '13 at 13:38
  • Hey there, I had this issue and searched in a lot of places and this PluralSight course covers this exact thing, so perhaps grab the free trial and give it a watch http://tinyurl.com/n68qdaz – Steve Newstead Aug 23 '13 at 11:23

7 Answers7

5

I'd argue that you are expecting a result from your test that implies the use of several dependencies, arguably not qualifying it as a unit test, especially because of an implied dependency on EF.

The idea here is that if you acknowledge that your BookService has a dependency on EF you should use a mock to assert it interacts correctly with it, unfortunately EF doesn't seem to like to be mocked, so we can always put it under a repository, here's an example of how that test could be written using Moq:

[Test]
public void AssignAuthorToBook_NewBookNewAuthor_CreatesNewBookAndAuthorAndAssociatesThem()
{
  var bookRepositoryMock = new Mock<IBookRepository>(MockBehavior.Loose);
  IBookService service = new BookService(bookRepositoryMock.Object);
  var book = new Book() {Id = 0}
  var author = new Author() {Id = 0}; 

  service.AssignAuthorToBook(book, author);

  bookRepositoryMock.Verify(repo => repo.AddNewBook(book));
  bookRepositoryMock.Verify(repo => repo.AddNewAuthor(author));
  bookRepositoryMock.Verfify(repo => repo.AssignAuthorToBook(book, author));
}

The id being set is something that you would use an integration test for, but I'd argue that you shouldn't worry about the EF failing to set the Id, I say this for the same reason you should not worry about testing if the .net framework does what it's supposed to do.

I've written about interaction testing in the past (which I think is the right way to go in this scenario, you are testing the interaction between the BookService and the Repository), hope it helps: http://blinkingcaret.wordpress.com/2012/11/20/interaction-testing-fakes-mocks-and-stubs/

Rui
  • 4,847
  • 3
  • 29
  • 35
  • I've just remembered, although this might not seem very useful other than for making a more interesting example: if you had a test in your book service that checked if the Id of the book was 0, it would first add the book (same for the author) and then call AssignBookToAuthor, all using the Repository's mock. This way you would be testing the behavior of adding the book and the author if they were new and then assigning them to each other, all without a call to the DB – Rui Aug 21 '13 at 09:05
  • Thanks. In our service implementation, the service is manipulating the domain objects directly (it could be `book.Author = author;`) rather than calling a repo method `repo.AssignAuthorToBook(book, author)`. The main problem with our tests is that they are testing business logic by the way the IDs / navigation properties are set on the domain objects, often a lot more complex than a simple author/book relationship, which is the main reason I'm bothered by the way it is done currently. – SilverlightFox Aug 21 '13 at 13:13
  • PS. Please could you update your answer with your suggestion in your comments? I'm not sure I follow - it would make your example clearer. – SilverlightFox Aug 21 '13 at 13:15
  • Just added the example that I mentioned in the comments – Rui Aug 21 '13 at 13:43
  • Let me put it this way, if you have a dependency on EF in BookService and you call SaveChanges in your method you probably can't unit test it (I'm not sure if is possible to fake EF, maybe it is, but it certainly won't be easy). On top of that if you use the repository pattern you will not only enable testability but you can also easily walk away from EF to another implementation if you so desire in the future. – Rui Aug 21 '13 at 13:51
  • I see your point - by manipulating domain objects directly from the service layer, the service layer is then dependent on EF (even though it is achieved via POCO) and cannot be tested via unit testing. To achieve what I'm after, the domain object manipulation would have to happen within my repositories. – SilverlightFox Aug 21 '13 at 14:30
  • Well, I see no problem with changing POCO values in the service layer. It is when you expect that to happen through a call to EF's SaveChanges that is the problem. – Rui Aug 21 '13 at 14:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/35927/discussion-between-silverlightfox-and-rui) – SilverlightFox Aug 21 '13 at 15:00
  • I've just had a though that I think summarizes your problem nicely. You are trying to adapt your tests to your code, when what you should do is adapt the way you code in order to enable testing. You are not giving up on the fact that the EF's navigation properties do something you can't control (and therefore test), if you acknowledge this, it is easy to see that putting that behind a dependency you can control is not as a burden, but something you have to do to enable testability. – Rui Aug 23 '13 at 09:04
2

I was having the same problem as you and came across your post. What I found after was a in memory database called Effort. Take a look at Effort

The following test works correctly

        EntityConnection conn = Effort.EntityConnectionFactory.CreateTransient("name=MyEntities");

        MyEntities ctx = new MyEntities(conn);

        JobStatus js = new JobStatus();
        js.JobStatusId = 1;
        js.Description= "New";

        ctx.JobStatuses.Add(js);

        Job j = new Job();
        j.JobId = 1;
        j.JobStatus = js;
        ctx.Jobs.Add(j);

        ctx.SaveChanges();

        Assert.AreEqual(j.JobStatusId, 1);

Where MyEntities is a DbContext created with a Effort connection string.

You still need to create you in memory objects, but, save changes on the context sets the objects associations as a database does.

Nick Crisp
  • 21
  • 2
1

That will never work the way you have it. The data context is wrapped up behind the scenes in your service layer, and the association of the BookID is never going to get updated on your local variable.

When I have done TDD on something using EF, I generally wrap up all my EF logic into some kind of DAO and have CRUD methods for the entities.

Then you could do something like this:

[Test]
public void AssignAuthorToBook_NewBookNewAuthor_SuccessfullyAssigned()
{
  IBookService service = new BookService();

  var book = new Book();
  var bookID = 122;
  book.ID = bookId;
  var Author = new Author() { Id = 123 };

  service.AssignAuthorToBook(book, author);

//ask the service for the book, which uses EF to get the book and populate the navigational properties, etc...
  book = service.GetBook(bookID)

  Assert.AreEqual(book.AuthorId, 123);
}
Joe Brunscheon
  • 1,949
  • 20
  • 21
  • Thanks for your answer, however your example would only work in an integration test - is that correct? I don't want the test to interact with the database. Question updated to clarify I'm looking for a solution that would work in a unit test. – SilverlightFox Aug 15 '13 at 09:52
1

Your question is: if you mock database operation, you cannot test the correct function of AssignAuthorToBook because this class is high coupled to Entity Framework and the behaviour will change.

So my solution: Decouple the classes (using a DAO, an interface with all database operations), now AssignAuthorToBook is easy to test because use functions of SetBook / GetBookId. And do a test for your operations (SetBook / GetBookId) testing the database (ok, it's not a clear unittesting, but your question is exactly this: who can I test a database operation?, so... testing it, but in a separate test.

user149113
  • 46
  • 4
  • For more complex objects, wouldn't this lead to bloating as there would be a `Set` and `Get` method for every property? Have you an example to explain better? Thanks. – SilverlightFox Aug 21 '13 at 07:51
1

The general solution is to split it into layers.

  • The persistence layer / repository pattern is in charge of writing out/reading in information from whatever store you choose. ORMs are supposed to be boxed inside the persistance layer. Above this layer, there should be no trace of the ORM. This layer returns entities/value objects as defined in the DDD book (I dont intend anything related to the EF)
  • Next the service layer calls onto the Repository interface to obtain these POCO entities/value objects and is in charge of domain/business logic.

As for the testing,

  • the basic purpose of the persistence layer is to persist the desired information.. e.g. write customer information to a file or DB. Hence integration testing this layer with the implementation specific tech (e.g. SQL) makes the most sense. If the tests pass but the layer isn't able to read/write to the actual SQL DB.. they are useless.
  • The service layer tests can mock the persistence layer entry interface (e.g. Repository interface) and verify the domain logic without any dependency on the persistence tech - which is the way it should be. Unit tests here.
Gishu
  • 134,492
  • 47
  • 225
  • 308
0

If you want to test that the "AssignAuthorToBook" works as expected then you can do it being completely DB ignorant - you a mock of the Book object and verify that the correct setter was called with correct value. Everything persistence related should be stubbed. To verify that a setter or a method was called you can use Moq or RhinoMocks.

An example can be found here: Rhino Mocks: AAA Synax: Assert property was set with a given type

Here is an example of how to verify alternative expectations: Using RhinoMocks, how can I assert that one of several methods was called?

Community
  • 1
  • 1
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • The problem is that there are two ways to do this: `Book.Author = author;` or `Book.AuthorId = author.Id;` - what do you test for? Granted, not important in the simple example, but for more complex operations your unit tests could fail via a code change even though the implementation still works. – SilverlightFox Aug 15 '13 at 10:05
  • In RhinoMocks, for example, you can verify that a particular setter was called with a particular value. I've added a link to an example. – BartoszKP Aug 15 '13 at 10:07
  • OK, but my point is which setter? `Book.Author` or `Book.AuthorId` - what if this changes in future - the code will still work with this subtle change, but the unit test will fail. – SilverlightFox Aug 15 '13 at 10:13
  • Either you test the behavior - then you expect one particular setter to be used, either the state - then you just verify that Book.AuthorId has correct value, assuming that if Book.Author was set, then AuthorId should also be correct. Regarding subtle changes in the future: that's the price of unit tests - changes to the code usually need to be followed by changes to the tests. However it seems that unit tests are still worth it. – BartoszKP Aug 15 '13 at 10:15
  • That's my question: How to mock AuthorId being set when Author is set and vice versa and whether this approach is right. I would like a generic solution that works with whatever the structure of my EF model happens to be (i.e. all associations wired up). I know this is possible by writing something (possibly complex) to do this, but if this has already been implemented, tried and tested then I'd rather use an existing solution in a unit test rather than something new and bespoke and possibly prone to failure. – SilverlightFox Aug 15 '13 at 10:22
  • Ok, sorry, I didn't understand you clearly enough in the first place :) I've added a link which allows you to verify alternative expectations - maybe this will help. – BartoszKP Aug 15 '13 at 10:32
0

I may be wrong, but as far as I know your domain model should be consistent even without a persistence layer.

So, in this case, your service, should ensure that the property AuthorID is equal to the Assigned Author class, even before persisting it to the database. Or your Book AuthorID property getter get that info from its inner Author class ID property assigned by the service.

public class Book {
    public Author Author { get; set; }
    public int AuthorId { 
        get { return Author.ID; }
        set { Author.ID = value; }
    }
}

public class Author {
    public int Id { get; set; }
}

public class BookService {
    public void AssignAuthorToBook(Book book, Author author)
    {
        book.Author = author;
    }
}
Roger Barreto
  • 2,004
  • 1
  • 17
  • 21