我正在尝试为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);
,然后发生了错误。
错误是因为您正在模拟接口并试图传递构造函数参数。这不会像错误消息所说的那样工作。
但是,您可以使用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);
//...
以下是我在《宇航员》中处理它的方法。
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
值设置为令牌来完成此操作。
请记住,FeedResponse
的ResponseContinuation
属性也将useETagAsContinuation
值考虑在内。在反射调用的构造函数中,我默认为false。
要获得任何进一步的参考,请检查项目的代码以及单元测试的编写方式。