Reputation: 1678
*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?
Why isn't this working?
Upvotes: 13
Views: 13976
Reputation: 5221
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));
}
Upvotes: 6
Reputation: 1678
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?
Upvotes: 1
Reputation: 30819
Since Moq uses inheritance in order to replace method calls you can only mock virtual methods (or interfaces).
So either make the methods/properties you're trying to fake virtual or use Isolator/JustMock etc. that work using Jit weaving and can fake these methods.
Upvotes: 0
Reputation: 3748
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
Upvotes: 2