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

我一直在尝试对这个简单的方法进行单元测试:


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);


}


精慕HU
浏览 239回答 1
1回答

喵喵时光机

你根本不必嘲笑ForEachAsync。ForEachAsync返回Task并异步执行这是您的问题的根源。使用async和await键来解决您的问题:public async void DeleteAllSettingsLinkedToSoftware(Guid softwareId){&nbsp; &nbsp; await _dbContext.Settings.Where(s => s.SoftwareId == softwareId)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.ForEachAsync(s => s.IsDeleted = true);&nbsp; &nbsp; _dbContext.SaveChanges();&nbsp;&nbsp;}编辑:出现新异常是因为提供Provider的不是IDbAsyncQueryProvider。Microsoft 实现了此接口的通用版本:TestDbAsyncQueryProvider<TEntity>. 这是链接中的实现:internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider&nbsp;{&nbsp;&nbsp; &nbsp; private readonly IQueryProvider _inner;&nbsp;&nbsp; &nbsp; internal TestDbAsyncQueryProvider(IQueryProvider inner)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; _inner = inner;&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public IQueryable CreateQuery(Expression expression)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return new TestDbAsyncEnumerable<TEntity>(expression);&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public IQueryable<TElement> CreateQuery<TElement>(Expression expression)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return new TestDbAsyncEnumerable<TElement>(expression);&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public object Execute(Expression expression)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return _inner.Execute(expression);&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public TResult Execute<TResult>(Expression expression)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return _inner.Execute<TResult>(expression);&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return Task.FromResult(Execute(expression));&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return Task.FromResult(Execute<TResult>(expression));&nbsp;&nbsp; &nbsp; }&nbsp;}&nbsp;internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>&nbsp;{&nbsp;&nbsp; &nbsp; public TestDbAsyncEnumerable(IEnumerable<T> enumerable)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; : base(enumerable)&nbsp;&nbsp; &nbsp; { }&nbsp;&nbsp; &nbsp; public TestDbAsyncEnumerable(Expression expression)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; : base(expression)&nbsp;&nbsp; &nbsp; { }&nbsp;&nbsp; &nbsp; public IDbAsyncEnumerator<T> GetAsyncEnumerator()&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return GetAsyncEnumerator();&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; IQueryProvider IQueryable.Provider&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; get { return new TestDbAsyncQueryProvider<T>(this); }&nbsp;&nbsp; &nbsp; }&nbsp;}&nbsp;internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>&nbsp;{&nbsp;&nbsp; &nbsp; private readonly IEnumerator<T> _inner;&nbsp;&nbsp; &nbsp; public TestDbAsyncEnumerator(IEnumerator<T> inner)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; _inner = inner;&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public void Dispose()&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; _inner.Dispose();&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public Task<bool> MoveNextAsync(CancellationToken cancellationToken)&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; return Task.FromResult(_inner.MoveNext());&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; public T Current&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; get { return _inner.Current; }&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; object IDbAsyncEnumerator.Current&nbsp;&nbsp; &nbsp; {&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; get { return Current; }&nbsp;&nbsp; &nbsp; }&nbsp;}&nbsp;现在Setup你必须像这样使用它:mockSet.As<IQueryable<Setting>>()&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.Setup(m => m.Provider)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;.Returns(new TestDbAsyncQueryProvider<Setting>(data.Provider));&nbsp;
打开App,查看更多内容
随时随地看视频慕课网APP