Unit Testing: TDD with POCO Objects with Navigation Properties (Relationship Repair)

I am trying to find a good solution for this, but no luck, so either I am not looking for suitable keywords, or we are doing something wrong from the very beginning, so the problem should not really exist.

Update for clarification: I would like this to work as a unit test, and not as an integration test, so I don't want this to get into the database, but I want to make fun of associations when EF saves the changes to my unit test.

The original question:

Suppose you are testing a service method as follows:

[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 let's say that this test failed because AssignAuthorToBook really works using the code book.Author = author; therefore it does not assign AuthorId , it assigns an entity. When this is saved using the Entity Framework SaveChanges() method in the context, it will bind entities and identifiers will correlate. However, in my example above, the logic of the Entity Framework would not be applied. I say that the code will run once when SaveChanges() was called, but the unit test will fail.

In this simple example, you will probably immediately find out why your test failed because you just wrote the test immediately before the code and could easily fix it. However, for more complex operations and operations in which future changes may change the way communications between objects occur, which will lead to test disruptions, but may not disrupt functionality, how is unit testing best suited?

My thoughts:

  • The level of service should be unfamiliar with the level of persistence. Should we mock the data context in unit tests in order to mock how it works? Is there an easy way to do this that will automatically associate the associations (i.e. Assign the correct entity if Id used or assign the correct Id if the object is used)?
  • Or should tests be structured somewhat differently?

Those tests that exist in the current project that I inherited work as in my example above, but it turns out to me that something is wrong with this approach, and that I could not find a simple solution for a possible common problem . I believe that the data context should be mocked, but it looks like you need to add a lot of code to the layout to dynamically create associations - has this been decided for sure?

Update: These are the closest answers I have found so far, but they are not quite what I need. I don’t want to test EF as such, I just wondered what is best suited for testing service methods that access repositories (either directly or through navigation properties through other repositories that use the same context).

How can I redo the Intelligent Intelligent Intelligent Integral (Entity Framework)?

Fixed datacontext and foreign keys / navigation properties

Fake DbContext for Entity Framework 4.1 for testing

Navigation Properties Not Set Using ADO.NET Mocking Context Generator

Conclusion:. This is not possible with the help of unit testing and is only possible with the help of testing integration with a real database. You can come close and probably encode something to dynamically associate navigation properties, but your data layout will not fully replicate the real context. I would be glad if any solution allowed me to automatically bind navigation properties that would allow my unit tests to be better, if not perfect (the nature of a successful unit test in no way guarantees functionality) ADO.NET Mocking Context Generator is close, but it seems that I will have to have a mock version of each entity that will not work for me in case functioanlity is added to them using partial classes in my implementation.

+6
source share
7 answers

I would say that you are expecting a test result that involves the use of several dependencies, possibly not qualifying it as a unit test, especially because of the implied dependence on EF.

The idea here is that if you acknowledge that your BookService has an EF dependency, you should use a layout to claim that it interacts correctly with it, unfortunately, EF does not seem to mock, so we can always put it in the repository, here is an example of how this test can 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 set identifier is what you would use for the integration test, but I would say that you should not worry that EF will not be able to set the identifier, I say this for the same reason that you should not worry if the .net infrastructure does what it should do.

I wrote about testing interaction in the past (which, I think, is the right way in this scenario, you are checking the interaction between BookService and the repository), I hope this helps: http://blinkingcaret.wordpress.com/2012/11/20 / interaction-testing-fakes-mocks-and-stubs /

+5
source

It will never work the way you do. The data context ends behind the scenes of your service level, and the connection with BookID will never be updated on your local variable.

When I was doing TDD on something using EF, I usually wrap up all my EF logic in some kind of DAO and have CRUD methods for entities.

Then you can 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); } 
+1
source

Your question: if you are mocking a database operation, you cannot check the correct AssignAuthorToBook function because this class is related to the Entity Framework and the behavior will change.

So, my solution: Parse the classes (using DAO, the interface with all database operations), now AssignAuthorToBook is easily tested because it uses the functions SetBook / GetBookId. And do a test for your operations (SetBook / GetBookId) testing the database (well, this is not an explicit unittesting, but your question is exactly this: who can I verify the operation with the database?), Therefore ... testing, but in a separate test.

+1
source

The general solution is to break it into layers.

  • The save / repository template is responsible for writing / reading information from any store you have selected. It is suggested that ORMs should be placed inside a resistance layer. No traces of ORM should be above this layer. This layer returns object / value objects defined in the DDD book (I do not intend to associate anything with EF)
  • Then the service level calls the repository interface to get these objects / values ​​of the POCO object and is responsible for the domain / business logic.

As for testing,

  • The main purpose of the save layer is to save the desired information. write client information to a file or database. Therefore, integration testing this level with specific implementation technologies (such as SQL) makes the most sense. If the tests pass, but the layer cannot read / write to the actual SQL database, they are useless.
  • Service level tests can scoff at the input level of the save level (for example, with the repository interface) and check the domain logic without any dependence on persistence technology, which should be so. Unit tests here.
+1
source

I had the same problem as yours and stumbled upon your post. What I found after was in a memory database called Effort. Take a look at Effort

The following test is working 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 using the Effort connection string.

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

+1
source

If you want to verify that "AssignAuthorToBook" works as you expected, you can do this by being completely unfamiliar with the DB - you are mocking the Book object and make sure that the correct setter was called with the correct value. All perseverance should be sharpened. To make sure the setter or method is being called, you can use Moq or RhinoMocks .

An example can be found here: Rhino Mocks: AAA Synax: Assert property set with the specified type

Here is an example of how to check for alternate expectations: Using RhinoMocks, how can I say that one of several methods was called?

0
source

Maybe I'm wrong, but as far as I know, your domain model should be consistent even without a save layer.

Thus, in this case, your service must ensure that the AuthorID property is equal to the Assigned Author class, even before it is stored in the database. Or your recipient of the AuthorID ID property of your book gets this information from its internal 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; } } 
0
source

Source: https://habr.com/ru/post/951735/


All Articles