无法将构造函数参数传递给接口模拟(模拟IFeedResponse)

时间:2018-08-07 22:38:27

标签: c# azure unit-testing moq azure-cosmosdb

我正在尝试为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);处停止,然后发生错误。

2 个答案:

答案 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。

有关更多参考,请检查项目代码以及如何编写单元测试。