C
C#2y ago
plam

❔ Mocking EF Core extensions methods.

I have actually tried a lot of stuff just in order to mock one of the methods which are used by Repository. Specifically, the issue is connected to using ContainsAsync. Since it's an extension method, I'm not able to mock it directly. I have tried using Wrapper pattern, especially wrapping DbSet and the ContainsAsync method itself. Furthermore, I have tried to implement it via using custom IAsyncEnumerator in addition to AsyncQueryProvider. I have also checked out two different libraries which do allow to mock stuff easier: MockQueryable.Moq & Moq.EntityFrameworkCore. Both of them didn't provide me with a proper result. Here's my repository method:
public async Task<OrderItem?> UpdateAsync(OrderItem orderItem, CancellationToken cancellationToken = default)
{
bool isContains = await _dbContext.OrderItems.ContainsAsync(orderItem, cancellationToken);
if (!isContains)
{
return null;
}

_dbContext.OrderItems.Update(orderItem);
await _dbContext.SaveChangesAsync(cancellationToken);

return orderItem;
}
public async Task<OrderItem?> UpdateAsync(OrderItem orderItem, CancellationToken cancellationToken = default)
{
bool isContains = await _dbContext.OrderItems.ContainsAsync(orderItem, cancellationToken);
if (!isContains)
{
return null;
}

_dbContext.OrderItems.Update(orderItem);
await _dbContext.SaveChangesAsync(cancellationToken);

return orderItem;
}
Any ideas how can I implement testing method UpdateAsync_ReturnsUpdatedOrderItem_WhenOrderItemExists()? Much appreciated in advance!
7 Replies
plam
plamOP2y ago
I have also tried out using AnyAsync in order to mock ContainsAsync in my DbSet setup, but it's also an extension method:
[Fact]
public async Task UpdateAsync_ReturnsUpdatedOrderItem_WhenOrderItemExists()
{
// Arrange
var orderItems = new List<OrderItem>
{
new OrderItem {Id = "1", Title = "Item 1", Description = "Description 1", Price = 9.99f, Quantity = 2},
new OrderItem {Id = "2", Title = "Item 2", Description = "Description 2", Price = 4.99f, Quantity = 1}
};

var mockDbContext = new Mock<ApplicationDbContext>(new DbContextOptions<ApplicationDbContext>());
var mockDbSet = orderItems.AsQueryable().BuildMockDbSet();

mockDbSet.Setup(x => x.AnyAsync(It.IsAny<Expression<Func<OrderItem, bool>>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((Expression<Func<OrderItem, bool>> predicate, CancellationToken cancellationToken)
=> orderItems.AsQueryable().Any(predicate.Compile()));


mockDbContext.Setup(x => x.OrderItems).Returns(mockDbSet.Object);

var updatedOrderItem = new OrderItem
{
Id = "2",
Title = "Updated Item 2",
Description = "Updated Description 2",
Price = 19.99f,
Quantity = 3
};

// Act
var result = await _orderItemRepository.UpdateAsync(updatedOrderItem);

// Assert
Assert.NotNull(result);
Assert.Equal(updatedOrderItem, result);

mockDbSet.Verify(x => x.Update(updatedOrderItem), Times.Once());
mockDbContext.Verify(x => x.SaveChangesAsync(default), Times.Once());
}
[Fact]
public async Task UpdateAsync_ReturnsUpdatedOrderItem_WhenOrderItemExists()
{
// Arrange
var orderItems = new List<OrderItem>
{
new OrderItem {Id = "1", Title = "Item 1", Description = "Description 1", Price = 9.99f, Quantity = 2},
new OrderItem {Id = "2", Title = "Item 2", Description = "Description 2", Price = 4.99f, Quantity = 1}
};

var mockDbContext = new Mock<ApplicationDbContext>(new DbContextOptions<ApplicationDbContext>());
var mockDbSet = orderItems.AsQueryable().BuildMockDbSet();

mockDbSet.Setup(x => x.AnyAsync(It.IsAny<Expression<Func<OrderItem, bool>>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((Expression<Func<OrderItem, bool>> predicate, CancellationToken cancellationToken)
=> orderItems.AsQueryable().Any(predicate.Compile()));


mockDbContext.Setup(x => x.OrderItems).Returns(mockDbSet.Object);

var updatedOrderItem = new OrderItem
{
Id = "2",
Title = "Updated Item 2",
Description = "Updated Description 2",
Price = 19.99f,
Quantity = 3
};

// Act
var result = await _orderItemRepository.UpdateAsync(updatedOrderItem);

// Assert
Assert.NotNull(result);
Assert.Equal(updatedOrderItem, result);

mockDbSet.Verify(x => x.Update(updatedOrderItem), Times.Once());
mockDbContext.Verify(x => x.SaveChangesAsync(default), Times.Once());
}
Unknown User
Unknown User2y ago
Message Not Public
Sign In & Join Server To View
plam
plamOP2y ago
yeah, I have been looking through this one Here I am not mocking any of extension methods, and due to this reason the test fails and returns null. Basically, need to mock ContainsAsync here, and the test will succeed.
[Fact]
public async Task UpdateAsync_ReturnsUpdatedOrderItem_WhenOrderItemExists()
{
// Arrange
var orderItems = new List<OrderItem>
{
new OrderItem {Id = "1", Title = "Item 1", Description = "Description 1", Price = 9.99f, Quantity = 2},
new OrderItem {Id = "2", Title = "Item 2", Description = "Description 2", Price = 4.99f, Quantity = 1}
};

var mockDbSet = orderItems.AsQueryable().BuildMockDbSet();
_mockDbContext.Setup(x => x.OrderItems).Returns(mockDbSet.Object);

var updatedOrderItem = new OrderItem
{
Id = "2",
Title = "Updated Item 2",
Description = "Updated Description 2",
Price = 19.99f,
Quantity = 3
};

// Act
var result = await _orderItemRepository.UpdateAsync(updatedOrderItem);

// Assert
Assert.NotNull(result);
Assert.Equal(updatedOrderItem, result);
}
[Fact]
public async Task UpdateAsync_ReturnsUpdatedOrderItem_WhenOrderItemExists()
{
// Arrange
var orderItems = new List<OrderItem>
{
new OrderItem {Id = "1", Title = "Item 1", Description = "Description 1", Price = 9.99f, Quantity = 2},
new OrderItem {Id = "2", Title = "Item 2", Description = "Description 2", Price = 4.99f, Quantity = 1}
};

var mockDbSet = orderItems.AsQueryable().BuildMockDbSet();
_mockDbContext.Setup(x => x.OrderItems).Returns(mockDbSet.Object);

var updatedOrderItem = new OrderItem
{
Id = "2",
Title = "Updated Item 2",
Description = "Updated Description 2",
Price = 19.99f,
Quantity = 3
};

// Act
var result = await _orderItemRepository.UpdateAsync(updatedOrderItem);

// Assert
Assert.NotNull(result);
Assert.Equal(updatedOrderItem, result);
}
Pretty tough question, I guess I will just use an in-memory database testing method for all my repositories.
jcotton42
jcotton422y ago
Don't Use the same database engine https://dotnet.testcontainers.org/ is great for spinning that up if it's not something like sqlite
Jayy
Jayy2y ago
you cant directly mock extensions
plam
plamOP2y ago
yeah that's what I literally mentioned
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server