C# 使用 MockQueryable.NSubstitute 模擬 Entity Framework Core 查詢

在單元測試時該如何測試資料庫?難道真的要建立一個替身資料庫?還是需要使用 SQLite 或是記憶體中的假資料庫(in-memory provider)?
但是那應該是整合測試的範疇了,我只是想要做單元測試,可以模擬查詢取得資料就好,有沒有最簡單的方法?

安裝

先使用 NuGet 安裝 MockQueryable.NSubstitute 套件,或是使用 .NET CLI 執行以下指令安裝
	
dotnet add package MockQueryable.NSubstitute
    


註: 本套件適用於 NSubstitute ,如果使用 Moq 則是安裝 MockQueryable.Moq

模擬 ApplicationDbContext

一開始要先模擬 ApplicationDbContext,而最好的方式就是建立一個介面,為了確保可以正常使用,需要有下面這三個方法,然後讓原本的 ApplicationDbContext 繼承這個 IApplicationDbContext
    
public interface IApplicationDbContext
{

	// TODO: 或是其他資料表
    // public DbSet<Calendar> Calendars { get; set; }
    
    public int SaveChanges();

    public Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken());

    public DatabaseFacade Database { get; }
}
    

在 Program.cs 也要增加:
    
builder.Services.AddScoped<IApplicationDbContext, ApplicationDbContext>();
    

模擬查詢結果

我們有一個資料表叫做 Calendar ,在 ApplicationDbContext 中可以使用 _context.Calendars 來做查詢,一般的查詢方式如下:
    
List<Calendar> list = _context.Calendars
    .Where(x => x.IsHoliday == true)
    .ToList();
    

使用 MockQueryable + NSubstitute 套件就可以使用下面的方式模擬假資料:
    
// 建立假資料
var mockCalendars = new List<Calendar>
{
    new() { Date = new DateTime(2023, 1, 1), IsHoliday = true },
    new() { Date = new DateTime(2023, 1, 2), IsHoliday = true },
};

// 建立假資料集
DbSet<Calendar> mockDbSet = mockCalendars.AsQueryable().BuildMockDbSet();

// 設定假資料集
_context.Calendars.Returns(mockDbSet);
    

接下來就可以使用平時查詢的方式,查詢結果就會很神奇的是剛剛設定的假資料了!
    
List<Calendar> list = _context.Calendars
    .Where(x => x.IsHoliday == true)
    .ToList();
    

留言