gpt4 book ai didi

unit-testing - 在使用 Linq 查询的单元测试中模拟 IDocumentQuery

转载 作者:行者123 更新时间:2023-12-04 15:17:50 25 4
gpt4 key购买 nike

我正在为 DocumentDBRepository 编写单元测试,但出现空引用异常。我使用 Moq 框架和 XUnit。

这是我在 DocumentDBRepository 类中的方法。

public class DocumentDBRepository<T> : IRepository<T> where T: class
{
private static string DatabaseId;
private static string CollectionId;
private static IDocumentClient client;
public DocumentDBRepository(IDocumentClient documentClient, string databaseId, string collectionId)
{
DatabaseId = databaseId;
CollectionId = collectionId;
client = documentClient;
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}

public async Task<IDocumentQuery<T>> GetQuery(Expression<Func<T, bool>> predicate)
{
try
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();

return query;
}
catch (Exception e) {
throw;
}
}

public async Task<IEnumerable<T>> GetEntities(IDocumentQuery<T> query)
{
try
{
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}

return results;
}
catch (Exception e)
{
throw;
}
}
}

这是我的测试代码:

public interface IFakeDocumentQuery<T> : IDocumentQuery<T>, IOrderedQueryable<T>
{

}

[Fact]
public async virtual Task Test_GetBooksById()
{

var expected = new List<Book> {
new Book { ID = "123", Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"} };


var response = new FeedResponse<Book>(expected);

var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();

mockDocumentQuery.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);

mockDocumentQuery.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);

var client = new Mock<IDocumentClient>();

client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);

var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");

//Act
var query = await documentsRepository.GetQuery(t => t != null);
var entities = await documentsRepository.GetEntities(query);

//Assert
if (entities != null)
{
entities.Should().BeEquivalentTo(expected);
}
}

这是运行测试方法后的错误消息:

Message: System.NullReferenceException : Object reference not set to an instance of an object.

当我单步执行代码时,错误发生在调用 GetQuery() 方法的测试代码之后:

 IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();

这是我的思考过程:当我单步执行整个代码时,我没有看到任何空变量。但是在测试方法第二行的“response”变量中,它确实显示了很多属性为空的异常,但结果 View 显示了“预期”变量。

我的问题是,是否是因为响应变量导致了空引用异常?还是其他地方?

PS:测试代码引用来自here

我还尝试将 Mock 行为打开为严格,并看到了此错误消息。

Message: System.AggregateException : One or more errors occurred. (IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.) ---- Moq.MockException : IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.

最佳答案

正如怀疑的问题是 .Where(predicate) 。我使用提供的示例进行了测试并删除了 .Where条款并执行至完成。

假接口(interface)继承自 IOrderedQueryableIDocumentQuery 。问题是Where正在将其转换回普通的 IEnumerable因为List数据源和 AsDocumentQuery正在崩溃,因为它期待 IDocumentQuery

我不喜欢与我无法控制的 API 紧密耦合。正是出于这个原因,我会围绕此类实现细节进行抽象。

解决方法包括必须提供一个假的 Linq IQueryProvider绕过任何查询并返回派生自 IDocumentQuery 的类型以便允许 AsDocumentQuery按预期行事。

但首先我重构了GetEntities并制作GetQuery private 来阻止存储库成为一个有漏洞的抽象。

private IDocumentQuery<T> getQuery(Expression<Func<T, bool>> predicate) {
var uri = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
var queryable = client.CreateDocumentQuery<T>(uri, feedOptions);
IQueryable<T> filter = queryable.Where(predicate);
IDocumentQuery<T> query = filter.AsDocumentQuery();
return query;
}

public async Task<IEnumerable<T>> GetEntities(Expression<Func<T, bool>> predicate) {
try {
IDocumentQuery<T> query = getQuery(predicate);
var results = new List<T>();
while (query.HasMoreResults) {
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
} catch (Exception e) {
throw;
}
}

请注意getQuery没有执行任何异步操作,因此它不应该返回 Task<>无论如何。

下一步在测试中被 mock IDocumentQuery被设置为允许测试顺利完成。这是通过提供模拟的 IQueryProvider 来完成的将返回 mock 的 IDocumentQuery当针对它调用 Linq 查询时。 (这就是问题开始的原因)

public async virtual Task Test_GetBooksById() {
//Arrange
var id = "123";
Expression<Func<Book, bool>> predicate = t => t.ID == id;
var dataSource = new List<Book> {
new Book { ID = id, Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"}
}.AsQueryable();

var expected = dataSource.Where(predicate);

var response = new FeedResponse<Book>(expected);

var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();

mockDocumentQuery
.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);

mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);

var provider = new Mock<IQueryProvider>();
provider
.Setup(_ => _.CreateQuery<Book>(It.IsAny<System.Linq.Expressions.Expression>()))
.Returns((Expression expression) => {
if (expression != null) {
dataSource = dataSource.Provider.CreateQuery<Book>(expression);
}
mockDocumentQuery.Object;
});

mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Provider).Returns(provider.Object);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Expression).Returns(() => dataSource.Expression);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.ElementType).Returns(() => dataSource.ElementType);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.GetEnumerator()).Returns(() => dataSource.GetEnumerator());

var client = new Mock<IDocumentClient>();

client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);

var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");

//Act
var entities = await documentsRepository.GetEntities(predicate);

//Assert
entities.Should()
.NotBeNullOrEmpty()
.And.BeEquivalentTo(expected);
}

这使得测试能够完成、按预期运行并通过测试。

关于unit-testing - 在使用 Linq 查询的单元测试中模拟 IDocumentQuery,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49906029/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com