Reputation: 3411
I am trying to write unit tests for DocumentDBRepository
paging code. Since there is continuation token involved in the FeedResponse
, I need to mock the FeedResponse
in order to put some value for FeedResponse.ContinuationToken
. But the problem is that I got an error saying:
Message: System.ArgumentException : Constructor arguments cannot be passed for interface mocks.
Does it mean I am not able to mock FeedResponse
? Or maybe the way I use FeedResponse
is wrong?
Here's my code:
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);
When I debugged, the break point stops at var response = new Mock<IFeedResponse<T>>(expected);
and then the error happened.
Upvotes: 2
Views: 3351
Reputation: 247413
The error is because you were mocking the interface and trying to pass a constructor argument. That wont work as stated by the error message.
You can however use an actual instance of FeedResponse
.
Given that the desired member is not virtual
and is also read-only, you could consider stubbing the class and overriding the default behavior since FeedResponse<T>
is not sealed
.
For example
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;
}
}
}
and using the stub in the test
//...
var token = ".....";
var response = new FeedResponseStub<T>(expected, token);
//...
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
//...
Upvotes: 3
Reputation: 7200
Here is the way I work around it in 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>();
}
}
You can pass your continuation token as a header key-value in the dictionary to set the FeedResponse value.
You can do that by setting the x-ms-continuation
value to a token.
Keep in mind that the ResponseContinuation
property of the FeedResponse
also takes the useETagAsContinuation
value into account. I default it to false in the reflection invoked constructor.
For any further reference check the project's code and how unit tests are written.
Upvotes: 1