Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing a method which contains a Using block

I have a already written (was written years ago) C# function, I have been asked to cover this method with Unit Tests.

public string PlaceOrder(int requestId, string orderedby)
    {
        try
        {
            using (DatabaseContext dbContext = new DatabaseContext("myConnectionStringHere"))
            {
                var req = dbContext.Orders.Where(row => row.id == requestId).FirstOrDefault();
                if (req == null)
                    return "not found";
               
                req.status="A";
                dbContext.SaveChanges();
                return "found";
            }
        }
        catch (Exception ex)
        {
            return "error";
        }
    }

Now while Unit testing I need to make sure that it does not write anything to database, so I have to MOQ it. How can I MOQ, it contains Using block.

I know architecture could have been better and design patterns should have been followed but I am not allowed to change the structure of the application as it is a legacy application.

like image 759
vndpal Avatar asked May 21 '26 14:05

vndpal


1 Answers

The general guidance here is to prefer Integration Test with in memory (w/o sqlite) database over unit testing.

Let me suggest you four helper libraries which can make your testing easier:

EntityFrameworkCoreMock

Github link

The prerequisite here is to mark your DbSet as virtual like this:

public virtual DbSet<Order> Orders { get; set; }

Then you can create a mock where you can populate your Orders collection with some dummy data:

var initialOrders = new[]
{
    new Order { ... },
    new Order { ... },
};

var dbContextMock = new DbContextMock<DatabaseContext>(new DbContextOptionsBuilder<DatabaseContext>().Options);
var ordersDbSetMock = dbContextMock.CreateDbSetMock(db => db.Orders, initialOrders);

You have to rewrite your containing class of the PlaceOrder method in a way to receive a DatabaseContext parameter in the constructor to be able inject dbContextMock.Object during testing.

In the assertion phase you can query your data and make assertion against it. Since you do not call Add, Remove or any other CRUD method, you can only Verify the SaveChanges call.

public void GivenAnExistingOrder_WhenICallPlaceOrder_ThenSaveChangesIsCalledOnce()
{
   ...
   //Assert
   dbMock.Verify(db => db.SaveChanges(), Times.Once);
}

public void GivenANonExistingOrder_WhenICallPlaceOrder_ThenSaveChangesIsCalledNever()
{
   ...
   //Assert
   dbMock.Verify(db => db.SaveChanges(), Times.Never);
}

EntityFrameworkCore.Testing

Github link

It is working more or less in the way as the previous library.

var dbContextMock = Create.MockedDbContextFor<DatabaseContext>();
dbContextMock.Set<Order>().AddRange(initialOrders);
dbContextMock.SaveChanges();

The assertions work in the same way.


A 3rd (less mature) library is called Moq.EntityFrameworkCore.


If you really keen to perform unit testing by avoiding in memory database then you should give a try to the MockQueryable library.

const int requestId = 1;
var orders = new List<Order>();
var ordersMock = orders.AsQueryable().BuildMockDbSet();
ordersMock.Setup(table => table.Where(row => row.Id == requestId)).Returns(...)

Here you are basically mocking what should be the result of your Where filter. In order to be able to use this the containing class of the PlaceOrder should receive a DbSet<Order> parameter via its constructor.

Or if you have an IDatabaseContext interface then you can use that one as well like this:

Mock<IQueryable<Order>> ordersMock = orders.AsQueryable().Build();
Mock<IDatabaseContext> dbContextMock = ...
dbContextMock.Setup(m => m.ReadSet<Order>()).Returns(ordersMock.Object));
like image 120
Peter Csala Avatar answered May 24 '26 05:05

Peter Csala



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!