How to use Moq to unit test a delete operation in entity framework 6
Solution 1
First edit your definition of requirements
to be a List<Requirement>
not a Queryable
to be able to mocking add or remove. And use requirements.AsQueryable()
in Setup
methods.
Second add this code to mocking remove:
mockDbSet.Setup(m => m.Remove(It.IsAny<Requirement>())).Callback<Requirement>((entity) => requirements.Remove(entity));
So you can check the count of your requirements
list after removing.
Your code should be like this:
[TestMethod]
public void DeleteRequirementSuccessfully()
{
var requirements = new List<Requirement>
{
new Requirement {
Id = 1,
Title = "Requirement 1",
Description = "Requirement 1 description"
},
new Requirement {
Id = 2,
Title = "Requirement 2",
Description = "Requirement 2 description"
},
new Requirement {
Id = 3,
Title = "Requirement 3",
Description = "Requirement 3 description"
}
};
var mockDbSet = new Mock<DbSet<Requirement>>();
var context = new Mock<RequirementsDatabaseEntities>();
// You should use .AsQueryable() in these lines
mockDbSet.As<IQueryable<Requirement>>()
.Setup(x => x.Provider)
.Returns(requirements.AsQueryable().Provider);
mockDbSet.As<IQueryable<Requirement>>()
.Setup(x => x.ElementType)
.Returns(requirements.AsQueryable().ElementType);
mockDbSet.As<IQueryable<Requirement>>()
.Setup(x => x.Expression)
.Returns(requirements.AsQueryable().Expression);
mockDbSet.As<IQueryable<Requirement>>()
.Setup(x => x.GetEnumerator())
.Returns(requirements.GetEnumerator());
// This line should be added
mockDbSet.Setup(m => m.Remove(It.IsAny<Requirement>())).Callback<Requirement>((entity) => requirements.Remove(entity));
context.Setup(x => x.Requirement).Returns(mockDbSet.Object);
var dataAccess = new RequirementsDataAccess(context.Object);
int idToDelete = 1;
dataAccess.DeleteRequirement(idToDelete);
context.VerifyGet(x => x.Requirement, Times.Exactly(2));
//mockDbSet.Verify(x => x.Remove(It.IsAny<Requirement>()), Times.Once());
context.Verify(x => x.SaveChanges(), Times.Once());
// add this Assert
Assert.AreEqual(requirement.Count, 2);
// or
Assert.IsFalse(requirement.Any(x => x.Id == idToDelete));
}
Solution 2
- it fails because you can't mock non virtual method.
- the same problem:
RequirementsDatabaseEntities.Requirement
is not virtual method than it provide different output in test method than you expected. it probably returns empty collection.
fix: make RequirementsDatabaseEntities.Requirement
getter virtual
Solution 3
Partial Solution - I found that the exception was just misleading. It was giving me this exception as I had got the number of times the mocked property was called wrong. It should have been called twice, instead of once. That part works now. But I still do not understand why the entity is not being removed from the list. Is it because it is queryable?
Related videos on Youtube
rideintothesun
Updated on March 08, 2020Comments
-
rideintothesun about 4 years
*Update Edit - Partial Solution - Help still needed * - I found that the exception was just misleading. It was giving me this exception as I had got the number of times the mocked property was called wrong. It should have been called twice, instead of once. That part works now.
But I still do not understand why the entity is not being removed from the list. Is it because it is queryable?
Original Question Below
I have been trying to follow this link to learn how to unit Entity Framework 6 and 6.1.
However it does not show how to unit test a delete operation. Here is the code I am trying to test:
public void DeleteRequirement(int id) { Requirement requirementToDelete = GetRequirement(id); context.Requirement.Remove(requirementToDelete); context.SaveChanges(); } public Requirement GetRequirement(int id) { return (from result in context.Requirement where result.Id == id select result).SingleOrDefault(); }
My unit test code is
[TestMethod] public void DeleteRequirementSuccessfully() { var requirements = new List<Requirement> { new Requirement { Id = 1, Title = "Requirement 1", Description = "Requirement 1 description" }, new Requirement { Id = 2, Title = "Requirement 2", Description = "Requirement 2 description" }, new Requirement { Id = 3, Title = "Requirement 3", Description = "Requirement 3 description" } } .AsQueryable(); var mockDbSet = new Mock<DbSet<Requirement>>(); var context = new Mock<RequirementsDatabaseEntities>(); mockDbSet.As<IQueryable<Requirement>>() .Setup(x => x.Provider) .Returns(requirements.Provider); mockDbSet.As<IQueryable<Requirement>>() .Setup(x => x.ElementType) .Returns(requirements.ElementType); mockDbSet.As<IQueryable<Requirement>>() .Setup(x => x.Expression) .Returns(requirements.Expression); mockDbSet.As<IQueryable<Requirement>>() .Setup(x => x.GetEnumerator()) .Returns(requirements.GetEnumerator()); context.Setup(x => x.Requirement).Returns(mockDbSet.Object); var dataAccess = new RequirementsDataAccess(context.Object); int idToDelete = 1; dataAccess.DeleteRequirement(idToDelete); context.VerifyGet(x => x.Requirement, Times.Exactly(2)); // <- now verification is correct mockDbSet.Verify(x => x.Remove(It.IsAny<Requirement>()), Times.Once()); context.Verify(x => x.SaveChanges(), Times.Once()); }
The test fails on the context.VerifyGet statement with the following error
Test method DataAccessTest.RequirementUnitTest+DeleteRequirement.DeleteRequirementSuccessfully threw exception: System.InvalidOperationException: No connection string named 'RequirementsDatabaseEntities' could be found in the application config file.
If I comment out
the context.VerifyGet
line the test passes, but the requirement is not deleted from the list. Does anyone have any idea why?- The test fails
- And why when I comment out the offending line, it passes but the requirement has not been deleted.
Why isn't this working?
-
default about 10 yearsjit.. weaving.. that sounds interesting. Do you have any links/documentation that describes it? My initial google search is a bit scarce (it shows aspect-weaving and run-time weaving.. not sure if that is the same?)
-
Jakub Krampl about 10 years#Default: If you're interested in these tools the .NET space has three at the moment: Typemock Isolator, Telerik's JustMock and MS Fakes (with VS2012/2013). All of these use the .NET profiler API to inject code during runtime which enables faking of virtually anything you want
-
rideintothesun about 10 yearsIn the link it sets up this method to be virtual. I think when using VS2013 EF 6.1 this is the default code generation template. Also would I not get an compile time error?
-
rideintothesun about 10 yearsIn the link it sets up this method to be virtual. I think when using VS2013 EF 6.1 this is the default code generation template. Also would I not get an compile time error?
-
Aik about 10 yearsYou have to check your generated code. There is more than one approach how to generate code and it should differ: Legacy, T4, Code First and maybe more. You don't get an error because mock type is constructed in runtime than compiler don't complain. Suppose new type only hide original non virtual method as you can do this as well in your inheritance. This is why I suppose your test method offer real property instead.
-
rideintothesun about 10 yearsI just checked, and it is virtual. The strange thing is that when I debug the test and the context.Requirement is called it does rerun the mocked DbContext (RequirementsDatabaseEntities). It's very strange.