我正在尝试为DocumentDBRepository
分页代码编写单元测试。由于FeedResponse
中包含连续令牌,因此我需要模拟FeedResponse
以便为FeedResponse.ContinuationToken
设置一些值。但是问题是我收到一个错误消息:
消息:System.ArgumentException:构造函数参数不能为 通过了接口模拟。
这是否意味着我无法嘲笑FeedResponse
?还是我使用FeedResponse
的方式是错误的?
这是我的代码:
var response = new Mock<IFeedResponse<T>>(expected);
response.Setup(_ => _.ResponseContinuation).Returns(It.IsAny<string>());
var mockDocumentQuery = new Mock<IFakeDocumentQuery<T>>();
mockDocumentQuery
.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
.Returns((Task<FeedResponse<T>>)response.Object);
当我调试时,断点在var response = new Mock<IFeedResponse<T>>(expected);
处停止,然后发生错误。
答案 0 :(得分:3)
该错误是因为您在模拟接口并尝试传递构造函数参数。如错误消息所述,该方法将无法正常工作。
但是,您可以使用FeedResponse
的实际实例。
鉴于所需成员不是virtual
并且也是只读的,您可以考虑对类进行存根并覆盖默认行为,因为FeedResponse<T>
不是sealed
。
例如
public class FeedResponseStub<T> : FeedResponse<T> {
private string token;
public FeedResponseStub(IEnumerable<T> result, string token)
: base(result) {
this.token = token;
}
public new string ResponseContinuation {
get {
return token;
}
}
}
并在测试中使用存根
//...
var token = ".....";
var response = new FeedResponseStub<T>(expected, token);
//...
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
//...
答案 1 :(得分:1)
这是我在Cosmonaut中解决它的方法。
public static FeedResponse<T> ToFeedResponse<T>(this IQueryable<T> resource, IDictionary<string, string> responseHeaders = null)
{
var feedResponseType = Type.GetType("Microsoft.Azure.Documents.Client.FeedResponse`1, Microsoft.Azure.DocumentDB.Core, Version=1.9.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var headers = new NameValueCollection
{
{ "x-ms-request-charge", "0" },
{ "x-ms-activity-id", Guid.NewGuid().ToString() }
};
if (responseHeaders != null)
{
foreach (var responseHeader in responseHeaders)
{
headers[responseHeader.Key] = responseHeader.Value;
}
}
var arguments = new object[] { resource, resource.Count(), headers, false, null };
if (feedResponseType != null)
{
var t = feedResponseType.MakeGenericType(typeof(T));
var feedResponse = Activator.CreateInstance(t, flags, null, arguments, null);
return (FeedResponse<T>)feedResponse;
}
return new FeedResponse<T>();
}
}
您可以将继续令牌作为标题键值传递到字典中,以设置FeedResponse值。
您可以通过将x-ms-continuation
的值设置为令牌来实现。
请记住,ResponseContinuation
的{{1}}属性也考虑了FeedResponse
值。我在反射调用的构造函数中将其默认设置为false。
有关更多参考,请检查项目代码以及如何编写单元测试。