[英]Unit Testing Service with Unit of Work with xunit .NET Core 3.1 and Moq
作為 xUnit 的新手,我有一個使用工作單元模式將數據返回到服務的應用程序,並且我正在嘗試對該服務進行單元測試。
這是服務代碼:
public async Task<IEnumerable<CatDto>> GetActiveCatsAsync()
{
var catList = new List<CatDto>();
var catEFList = await _uow.Repository<Cat>().GetActiveCatsAsync();
catList.AddRange(catEFList.Select(c => new CatDto
{
CatId = c.CatId,
CatName = c.CatName,
DisplayOrd = c.DisplayOrd
}));
return catList;
}
這是此服務調用的擴展:
public static async Task<List<Cat>> GetActiveCatsAsync(this IRepository<Cat> repository)
{
return await repository.AsQueryableRepo().Items.Where(c =>c.IsActive == true).OrderBy(c=>c.DisplayOrd).ToListAsync();
}
}
這是擴展使用的接口:
internal static IQueryableRepository<T> AsQueryableRepo<T>(this IRepository<T> repository) where T : class
{
return (IQueryableRepository<T>)repository;
}
下面是具體的工作單元 class:
public class SQLUnitOfWork<TContext> : BaseUnitOfWork, ISQLUnitOfWork<TContext>
where TContext : IDbContext, IDisposable
{
public SQLUnitOfWork(TContext context) : base(context)
{
}
}
到目前為止,這是我進行單個單元測試的地方:
[Fact]
public void GetActiveCatsAsync_ReturnsTypeIEnumerableCatDto()
{
var mockLogger = new Mock<ILogger<CatService>>();
var mockUoW = new Mock<ISQLUnitOfWork>();
var catServ = new CatService(mockLogger.Object, mockUoW.Object);
var result = catServ.GetActiveCatsAsync();
Assert.IsType<Task<IEnumerable<CatDto>>>(result);
}
該測試實際上通過了,但顯然“結果”變量中沒有數據,因為沒有設置模擬。 我也嘗試設置:
var mockRepo = new Mock<IRepository<Cat>>();
但這不允許我使用上面的擴展方法。 它沒有顯示為我提供返回值的“asQueryableRepo()”方法。 所以我不確定如何模擬返回數據,這將是一個簡單的 Cat object,在服務調用中顯示了三個屬性。 我也嘗試過使用 xUnit 的.ReturnAsync() 和 Task.FromResult() 功能。
經過幾次搜索,我嘗試實現 xUnit.DependencyInjection,但這需要在測試項目的 Startup.cs 文件中進行配置。 我的測試項目(使用 Visual Studio xUnit 項目模板創建)沒有 Startup.cs 文件。 我是否使用了錯誤的模板?
我還看到了類似於以下使用的東西:
scope = new CustomWebApplicationFactory<Startup>().Server.Host.Services.CreateScope();
service = scope.ServiceProvider.GetRequiredService<IDashboardService>();
context = scope.ServiceProvider.GetRequiredService<DTBContext>();
這似乎也引用了一家初創公司 class,所以我真的無法讓這種方法發揮作用。 我是否需要從包含 Startup.cs 文件夾的項目重新開始? 或者我可以在沒有它的情況下對上面的代碼進行單元測試嗎? 我仍在研究這個,但到目前為止我找不到模擬服務調用和工作單元元素的依賴關系的方法。
任何建議或意見將不勝感激! 謝謝!
更新:我想我現在了解 xUnit.DependencyInjection,所以我添加了 startup.cs class,並添加了 GitHub 頁面上提到的參考。 我還必須安裝 Microsoft.Extensions.Hosting package 來處理“沒有實現”錯誤,現在我的項目啟動了,上面通過的測試現在通過了。 我希望我現在可以處理測試項目中的依賴關系,但我會發現的。
您可以嘗試使用As
:
[Fact]
public async Task GetActiveCatsAsync_ReturnsTypeIEnumerableCatDto()
{
var mockLogger = new Mock<ILogger<CatService>>();
var mockRepo = new Mock<IRepository<Cat>>();
var cat = new Cat
{
IsActive = true
};
mockRepo
.As<IQueryableRepository<Cat>>()
.Setup(x => x.Items)
.Returns(x => new[] { cat }.AsQueryable);
var mockUoW = new Mock<ISQLUnitOfWork>();
mockUoW.Setup(x => x.Repository<Cat>()).Returns(mockRepo.Object);
var catServ = new CatService(mockLogger.Object, mockUoW.Object);
var result = await catServ.GetActiveCatsAsync();
var single = Assert.Single(result);
Assert.Equal(cat, single);
}
更新:
是的,測試實體框架是一個單獨的問題。 在這種情況下,我建議使用InMemoroyDatabase
或其他東西單獨測試存儲庫,這不適合用於服務測試。
要真正測試服務正在做它應該做的事情,理想情況下你會像這樣驗證:
[Fact]
public async Task GetActiveCatsAsync_ReturnsTypeIEnumerableCatDto()
{
var mockLogger = new Mock<ILogger<CatService>>();
var mockRepo = new Mock<IRepository<Cat>>();
var mockUoW = new Mock<ISQLUnitOfWork>();
mockUoW.Setup(x => x.Repository<Cat>()).Returns(mockRepo.Object);
var catServ = new CatService(mockLogger.Object, mockUoW.Object);
var result = await catServ.GetActiveCatsAsync();
mockRepo.Verify(x => x.GetActiveCatsAsync(), Times.Once);
}
但是您將無法在擴展方法上執行此操作。 不幸的是,您正在測試的代碼並不適合輕松編寫針對它的純單元測試。 具體來說,讓 UoW 公開存儲庫很麻煩。 讓ISQLUnitOfWork
擁有自己的GetActiveCatsAsync
方法會更容易,因此存儲庫將不可見(抽象泄漏) - 這樣您就可以直接在 UoW 上進行驗證:
mockUoW.Verify(x => x.GetActiveCatsAsync(), Times.Once);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.