为了账号安全,请及时绑定邮箱和手机立即绑定

调用EF Core扩展方法的c#单元测试方法

调用EF Core扩展方法的c#单元测试方法

C#
精慕HU 2021-07-01 13:00:16
我一直在尝试对这个简单的方法进行单元测试:public void DeleteAllSettingsLinkedToSoftware(Guid softwareId){    _dbContext.Settings.Where(s => s.SoftwareId == softwareId).ForEachAsync(s => s.IsDeleted = true);    _dbContext.SaveChanges();}但是,从ForEachAsync()调用该方法的那一刻起,我就很难对其进行单元测试。到目前为止,我已经使用 Moq 来设置 dbContext 以在Where()执行时返回正确的设置。我的尝试:Setup(m => m.ForEachAsync(It.IsAny<Action<Setting>>(), CancellationToken.None));我的问题是:我将如何对该ForEachAsync()方法的调用进行单元测试?我在网上读到有人说不可能对一些静态方法进行单元测试,如果在我的情况下是这样,我很好奇尽可能多地测试这种方法的替代方法。编辑我的完整测试代码:[TestMethod]public async Task DeleteAllSettingsLinkedToSoftware_Success(){    //Arrange    var settings = new List<Setting>    {        new Setting        {            SoftwareId = SoftwareId1        },        new Setting        {            SoftwareId = SoftwareId1        },        new Setting        {            SoftwareId = SoftwareId1        },        new Setting        {            SoftwareId = SoftwareId2        }    }.AsQueryable();    var queryableMockDbSet = GetQueryableMockDbSet(settings.ToList());    queryableMockDbSet.As<IQueryable<Setting>>()        .Setup(m => m.Provider)        .Returns(new TestDbAsyncQueryProvider<Setting>(settings.Provider));    DbContext.Setup(m => m.Settings).Returns(queryableMockDbSet.Object);    _settingData = new SettingData(DbContext.Object, SettingDataLoggerMock.Object);    //Act    var result = await _settingData.DeleteAllSettingsLinkedToSoftwareAsync(SoftwareId1);    //Assert    DbContext.Verify(m => m.Settings);    DbContext.Verify(m => m.SaveChanges());    Assert.AreEqual(4, DbContext.Object.Settings.Count());    Assert.AreEqual(SoftwareId2, DbContext.Object.Settings.First().SoftwareId);}
查看完整描述

1 回答

?
喵喵时光机

TA贡献1846条经验 获得超7个赞

你根本不必嘲笑ForEachAsync。ForEachAsync返回Task并异步执行这是您的问题的根源。


使用async和await键来解决您的问题:


public async void DeleteAllSettingsLinkedToSoftware(Guid softwareId)

{

    await _dbContext.Settings.Where(s => s.SoftwareId == softwareId)

                             .ForEachAsync(s => s.IsDeleted = true);

    _dbContext.SaveChanges();  

}

编辑:


出现新异常是因为提供Provider的不是IDbAsyncQueryProvider。


Microsoft 实现了此接口的通用版本:TestDbAsyncQueryProvider<TEntity>. 这是链接中的实现:


internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider 

    private readonly IQueryProvider _inner; 


    internal TestDbAsyncQueryProvider(IQueryProvider inner) 

    { 

        _inner = inner; 

    } 


    public IQueryable CreateQuery(Expression expression) 

    { 

        return new TestDbAsyncEnumerable<TEntity>(expression); 

    } 


    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 

    { 

        return new TestDbAsyncEnumerable<TElement>(expression); 

    } 


    public object Execute(Expression expression) 

    { 

        return _inner.Execute(expression); 

    } 


    public TResult Execute<TResult>(Expression expression) 

    { 

        return _inner.Execute<TResult>(expression); 

    } 


    public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) 

    { 

        return Task.FromResult(Execute(expression)); 

    } 


    public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) 

    { 

        return Task.FromResult(Execute<TResult>(expression)); 

    } 


internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T> 

    public TestDbAsyncEnumerable(IEnumerable<T> enumerable) 

        : base(enumerable) 

    { } 


    public TestDbAsyncEnumerable(Expression expression) 

        : base(expression) 

    { } 


    public IDbAsyncEnumerator<T> GetAsyncEnumerator() 

    { 

        return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); 

    } 


    IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() 

    { 

        return GetAsyncEnumerator(); 

    } 


    IQueryProvider IQueryable.Provider 

    { 

        get { return new TestDbAsyncQueryProvider<T>(this); } 

    } 


internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> 

    private readonly IEnumerator<T> _inner; 


    public TestDbAsyncEnumerator(IEnumerator<T> inner) 

    { 

        _inner = inner; 

    } 


    public void Dispose() 

    { 

        _inner.Dispose(); 

    } 


    public Task<bool> MoveNextAsync(CancellationToken cancellationToken) 

    { 

        return Task.FromResult(_inner.MoveNext()); 

    } 


    public T Current 

    { 

        get { return _inner.Current; } 

    } 


    object IDbAsyncEnumerator.Current 

    { 

        get { return Current; } 

    } 

现在Setup你必须像这样使用它:


mockSet.As<IQueryable<Setting>>() 

       .Setup(m => m.Provider) 

       .Returns(new TestDbAsyncQueryProvider<Setting>(data.Provider)); 


查看完整回答
反对 回复 2021-07-17
  • 1 回答
  • 0 关注
  • 238 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信