如何在 CosmosSDK v3+ 的 FeedResponse 中提供模拟值?
How to provide mock values in a FeedResponse for CosmosSDK v3+?
我正在为我的应用程序编写数据访问层,并尝试模拟 CosmosDB SDK 依赖项以进行单元测试。我将 NUnit 与 NSubstitute 一起使用,遇到了我试图模拟 Container.GetItemQueryIterator 的 return 值的问题。
我已成功提供模拟 feedIterator 作为该调用的响应,并提供模拟 feedResponse 作为 feedIterator.ReadNextAsync 的 return 值,但我不知道如何将任何类型的值注入要测试的 FeedResponse
我要测试的代码如下所示:
var feedIterator = container.GetItemQueryIterator<T>(queryDefinition);
while (feedIterator.HasMoreResults){
result.success = true;
foreach (var item in await feedIterator.ReadNextAsync()){
list.Add(item);
}
}
我尝试模拟这样的依赖关系(简化):
this.mockFeedResponse = Substitute.For<FeedResponse<T>>(this.mockApplicationList);
this.mockFeedIterator = Substitute.For<FeedIterator<T>>();
this.mockFeedIterator.ReadNextAsync().ReturnsForAnyArgs(Task.FromResult(this.mockFeedResponse));
this.mockFeedIterator.HasMoreResults.Returns(true);
查看 AzureCosmosDB SDK 文档,似乎有一个用于模拟的 FeedResponse 构造函数,它将 IEnumerable 作为参数,但是 NSubstitute 抱怨说当我尝试将列表传递给采用。是否有替代方法可以将一些 IEnumerable 作为 FeedResponse 提供?我哪里错了?
物有所值 - 我通过更改代码以访问从 CosmosDB 收到的 FeedResponse 上的 Resource 字段来解决此问题。在我的测试中,我能够模拟 Resource 的 return 值并获得所需的结果。
您可以模拟 ReadNextAsync
到 return 只定义了 GetEnumerator()
的 FeedResponse
的模拟。然后,您创建的 List
的 GetEnumerator
将传递给 foreach
.
的底层实现
以下示例使用 Moq
,但您应该能够在您的实现中执行类似的操作。
var mockFeedResponse = new Mock<FeedResponse<Thing>>();
mockFeedResponse
.Setup(x => x.GetEnumerator())
.Returns(
new List<Thing>
{
new Thing(),
new Thing()
}.GetEnumerator()
);
我还通过在 FeedResponse
模拟上模拟 GetEnumerator
调用来解决这个问题。但是,要记住的一点是,如果您只设置 mockFeedIterator.HasMoreResults(true)
,您将陷入无限循环。
我使用 Moq 的 Callback
方法功能解决了这个问题。我将 HasMoreResults
方法配置为 return true,然后在 ReadNextAsync
方法上设置回调以将 HasMoreResults
重新配置为 return false。这样,它将第一次进入 while
循环,根据模拟的 GetEnumerator
方法填充 return 集合,然后退出循环并 return 该集合到测试方法。
var myItems = new List<MyItem>
{
new MyItem(),
new MyItem()
};
var feedResponseMock = new Mock<FeedResponse<MyItem>>();
feedResponseMock.Setup(x => x.GetEnumerator()).Returns(myItems.GetEnumerator());
var feedIteratorMock = new Mock<FeedIterator<MyItem>>();
feedIteratorMock.Setup(f => f.HasMoreResults).Returns(true);
feedIteratorMock
.Setup(f => f.ReadNextAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(feedResponseMock.Object)
.Callback(() => feedIteratorMock
.Setup(f => f.HasMoreResults)
.Returns(false));
var containerMock = new Mock<Container>();
containerMock
.Setup(c => c.GetItemQueryIterator<MyItem>(
It.IsAny<QueryDefinition>(),
It.IsAny<string>(),
It.IsAny<QueryRequestOptions>()))
.Returns(feedIteratorMock.Object);
我正在为我的应用程序编写数据访问层,并尝试模拟 CosmosDB SDK 依赖项以进行单元测试。我将 NUnit 与 NSubstitute 一起使用,遇到了我试图模拟 Container.GetItemQueryIterator 的 return 值的问题。
我已成功提供模拟 feedIterator 作为该调用的响应,并提供模拟 feedResponse 作为 feedIterator.ReadNextAsync 的 return 值,但我不知道如何将任何类型的值注入要测试的 FeedResponse
我要测试的代码如下所示:
var feedIterator = container.GetItemQueryIterator<T>(queryDefinition);
while (feedIterator.HasMoreResults){
result.success = true;
foreach (var item in await feedIterator.ReadNextAsync()){
list.Add(item);
}
}
我尝试模拟这样的依赖关系(简化):
this.mockFeedResponse = Substitute.For<FeedResponse<T>>(this.mockApplicationList);
this.mockFeedIterator = Substitute.For<FeedIterator<T>>();
this.mockFeedIterator.ReadNextAsync().ReturnsForAnyArgs(Task.FromResult(this.mockFeedResponse));
this.mockFeedIterator.HasMoreResults.Returns(true);
查看 AzureCosmosDB SDK 文档,似乎有一个用于模拟的 FeedResponse 构造函数,它将 IEnumerable 作为参数,但是 NSubstitute 抱怨说当我尝试将列表传递给采用。是否有替代方法可以将一些 IEnumerable 作为 FeedResponse 提供?我哪里错了?
物有所值 - 我通过更改代码以访问从 CosmosDB 收到的 FeedResponse 上的 Resource 字段来解决此问题。在我的测试中,我能够模拟 Resource 的 return 值并获得所需的结果。
您可以模拟 ReadNextAsync
到 return 只定义了 GetEnumerator()
的 FeedResponse
的模拟。然后,您创建的 List
的 GetEnumerator
将传递给 foreach
.
以下示例使用 Moq
,但您应该能够在您的实现中执行类似的操作。
var mockFeedResponse = new Mock<FeedResponse<Thing>>();
mockFeedResponse
.Setup(x => x.GetEnumerator())
.Returns(
new List<Thing>
{
new Thing(),
new Thing()
}.GetEnumerator()
);
我还通过在 FeedResponse
模拟上模拟 GetEnumerator
调用来解决这个问题。但是,要记住的一点是,如果您只设置 mockFeedIterator.HasMoreResults(true)
,您将陷入无限循环。
我使用 Moq 的 Callback
方法功能解决了这个问题。我将 HasMoreResults
方法配置为 return true,然后在 ReadNextAsync
方法上设置回调以将 HasMoreResults
重新配置为 return false。这样,它将第一次进入 while
循环,根据模拟的 GetEnumerator
方法填充 return 集合,然后退出循环并 return 该集合到测试方法。
var myItems = new List<MyItem>
{
new MyItem(),
new MyItem()
};
var feedResponseMock = new Mock<FeedResponse<MyItem>>();
feedResponseMock.Setup(x => x.GetEnumerator()).Returns(myItems.GetEnumerator());
var feedIteratorMock = new Mock<FeedIterator<MyItem>>();
feedIteratorMock.Setup(f => f.HasMoreResults).Returns(true);
feedIteratorMock
.Setup(f => f.ReadNextAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(feedResponseMock.Object)
.Callback(() => feedIteratorMock
.Setup(f => f.HasMoreResults)
.Returns(false));
var containerMock = new Mock<Container>();
containerMock
.Setup(c => c.GetItemQueryIterator<MyItem>(
It.IsAny<QueryDefinition>(),
It.IsAny<string>(),
It.IsAny<QueryRequestOptions>()))
.Returns(feedIteratorMock.Object);