How to instantiate ResourceResponse<Document> - c#

I am trying to mock a call that returns ResourceResponse<Document>, but I am not able to instantiate that type. Is there a factory class that can instantiate it or some other way to do so?
EDIT
var response = new ResourceResponse<Document>();
The type 'Microsoft.Azure.Documents.Client.ResourceResponse' has no constructors defined

The latest stable version of Microsoft.Azure.DocumentDB (1.10.0) atm added 2 constructors for mocking purposes.
https://msdn.microsoft.com/en-us/library/azure/dn799209.aspx#Anchor_2
Edit
Using Moq you could do something like this:
Mock<IDocumentClient> documentClient = new Mock<IDocumentClient>();
documentClient
.Setup(dc => dc.ReplaceDocumentAsync(UriFactory.CreateDocumentUri("database", "collection", "id"), object, null) // last parameter are RequestOptions, these are null by default
.Returns(Task.FromResult(new ResourceResponse<Document>()));
This way I can check if the method on my documentClient is being called, if you want to influence what is returned in the document, you have to create a document, and following that a ResourceResponse of that document. Something like:
var document = new Document();
document.LoadFrom(jsonReader); // the json reader should contain the json of the document you want to return
Mock<IDocumentClient> documentClient = new Mock<IDocumentClient>();
documentClient
.Setup(dc => dc.ReplaceDocumentAsync(UriFactory.CreateDocumentUri("database", "collection", "id"), object, null) // last parameter are RequestOptions, these are null by default
.Returns(Task.FromResult(new ResourceResponse<Document>(document)));

The ResourceResponse class has the constructor that contains the DocumentServiceResponse parameter marked as internal.
This is bad because even though you can create a ResourceReponse object from your DTO class you cannot set things like RUs consumed, response code and pretty much anything else because they are all coming from the ResourceResponseBase which also has the DocumentServiceResponse marked as internal.
Please find code at below link
how-to-mock-or-instantiate

Kinda late but with Microsoft.Azure.DocumentDB.Core 2.4.2 there's a DocumentClient constructor that accepts an HttpMessageHandler as a parameter. It's ugly (so much so it deserves a downvote) but you can use this to inject responses with status codes. As far as I can tell nothing's leaving my box when I do this.
I also had to create a test-only constructor since I would never want to use this during running code. But for anyone still interested until a better library comes out here are the relevant parts.
Create your client
var client = new DocumentClient(host, authText, handler);
In your tests you can use a mocked handler like this:
public static Mock<HttpMessageHandler> CreateHttpMessageHandler(List<HttpResponseMessage> responses)
{
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock.Protected()
.Setup<Task<HttpResponseMessage>>(
nameof(HttpClient.SendAsync),
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => {
var response = responses[0];
responses.RemoveAt(0);
return response;
})
.Verifiable();
return handlerMock;
}
I created this response factory simulator
private HttpResponseMessage Respond(string text)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(text),
};
return response;
}
and set up the handler with something like this:
var responses = new List<HttpResponseMessage> {
Respond("{ ... }"),
Respond("{ ... }"),
Respond("{ ... }"),
Respond("{ ... }"),
};
Replace the ... with your Fiddler (or similar) trace output from an actual call to your CosmosDB. FWIW I had to supply 2 copies of my actual payload as the final 2 responses. This is obviously AS-IS and SUBJECT TO CHANGE but It worked for me. Hope it helps you and looking forward to a modern library designed to interfaces with SOLID principles.

Related

RestRequest Parameters adding logic

I am having some issues finding information about adding some logic field in my RestRequest using V 107. I am trying to add a filter to my GET query
dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'
There are a few other queries in the call which i am using Dictionary<string, string> to store them, and it works great however it only works if i am looking for something equal to, as adding it to the parameters it seems by default its equal to and i am not finding any way to add any other logic, gt/ge/lt/le etc. using the older version i would just append the url adding the logic i need, but i am not seeing a way to append the url either. Looking over their documentation i either missed it, cant find it, or its not there. Any help would be greatly appreciated! My method looks like this
public static async Task<string> GET_API(String RequestUrl, string RequestObject, Dictionary<string, string> parameters)
{
var request = new RestRequest(RequestObject);
var options = new RestClientOptions(RequestUrl)
{
ThrowOnAnyError = true,
Timeout = -1
};
var client = new RestClient(options);
client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("Bearer " + TokenManager.GetAccessTokenString("TRN"));
foreach (var parameter in parameters)
{
request.AddQueryParameter(parameter.Key, parameter.Value);
}
var response = await client.GetAsync(request);
return response.Content.ToString();
}
I send the BaseURL , the RequestObject would be table i am calling in the base URL, and my dictionary item contains the Field name, and the field values that i am dynamically generating on another method that would append the string. and example would be
parameters.Add("dl_document_name", "TableA");
which would append the URL with dl_document_name eq 'TableA'
it would call the API after i add the OAuth Token i create and return the data i need and send it back. or another option i guess could be appending the string with the logic i need to return the data
You should use OData, it's easy to implement and it has different kind of filters, you also can set which filters are usable and which aren't.
https://www.odata.org/
I figured out a work around, if i only have one i can add it to the first parameter and adding the filter as the first key, which will work unless i have multiple conditions that are not eq
parameters.Add("filter","dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'");

How to moq HttpClientExtensions method "PostAsJsonAsync"?

I need to write the unit test case around PostAsJsonAsync, which is extension method in HttpClientExtensions. I don't find any easy way to moq it.
Can someone help me in this.
Two ways I can think of:
Use a framework like Moles: https://www.microsoft.com/en-us/research/project/moles-isolation-framework-for-net/ This way you can replace extensions or any other methods with your own, returning the value you want
This is my preferred way. In such cases, wrap the service inside a proxy Interface. You might find it called adapter pattern elsewhere but in my opinion you are just abstracting the actions and proxying the data.
So create the IHttpClientProxy and the corresponding concrete implementation that will use any extention you like. Pass the IHttpClientProxy to your class and mock it as you like.
As mentioned in the comments, there is no way the mocking frameworks to mock static methods this way. Frameworks like Moq, only mock virtual or abstract methods (interface methods are inherently abstract) to guide towards better design.
The issue for me was that I was not understanding that PostAsJsonAsync was actually a convenience method that ultimately calls SendAsync on the handler. So many of the answers you find relating to mocking HTTP client are accurate. The gist is that you are indeed mocking the HttpMessageHandler, and using a concrete HttpClient. The missing piece was that you still need to perform setup and verification on 'SendAsync', not PostAsJsonAsync. Here is the code that worked for me to test PostAsJsonAsync, because SendAsync gets called on the handler:
// Arrange
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();
// new up a real HttpClient, passing in the mocked handler
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://example.com")
};
// replace with an instance of your actual concrete client
var yourClient = new YourClientHere(httpClient);
// Act
// perform the action on yourClient that makes the PostAsJsonAsync call
// Assert
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(accessRights.Count),
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
);
This article helped me get everything set up correctly: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
I am using NSubstitute for mocking this extension method on HttpClient and it seems seems to work fine without the use of Moles or an adapter.
public class ApiClientTests
{
private HttpClient _client;
private string _url;
private ModelDto _testModel;
public void ApiClientTests()
{
_client = Substitute.For<HttpClient>();
_client.BaseAddress = new Uri("http://fakeUrl/api/")
_url = "Models/";
_testModel = new ModelDto
{
Id = 1,
Name = "Model Name",
Description = "Model Description",
Outputs = new Dictionary<string, ModelOutputDto>(),
Parameters = new Dictionary<string, ModelParamDto>(),
Active = true
};
}
[Fact]
public async Task CreateItemAsync_ValidResponseCode_ReturnsNewResourceUri()
{
// Arrange
var returnUri = $"{_client.BaseAddress}{_url}";
var returnThis = new HttpResponseMessage(System.Net.HttpStatusCode.Created);
returnThis.Headers.Location = new Uri(returnUri);
_client.PostAsJsonAsync(_url, _testModel).ReturnsForAnyArgs(Task.FromResult(returnThis));
var apiClient = new ApiClient<ModelDto>(_client);
// Act
var result = await apiClient.CreateItemAsync(_testModel, _url);
// Assert
await _client.ReceivedWithAnyArgs().PostAsJsonAsync(_url, _testModel);
result.AbsoluteUri.Should().BeEquivalentTo(returnUri);
}

Blazor handle the error in the response from Web Api

There are two applications:
Blazor server-side
Web API (written long ago)
WebApi action
public async Task<object> GetAllAsync(...)
{
...
// Какая то проверка
throw new Exception("Что то пошло не так");
...
}
An example view of a method in a client application(Blazor server-side)
public async Task GetAllAsync()
{
var httpClient = clientFactory.CreateClient();
var responseMessage = await httpClient.GetAsync($"{address}/api/foo");
if (responseMessage.IsSuccessStatusCode)
{
// successfully
}
else
{
// How to get the error message here?
}
}
}
The question is: how to properly handle this kind of error from API?
ps
var exception = await responseMessage.Content.ReadAsAsync<HttpError>();
HttpError pulls a dependency with .NetFramework 4.6 (but initially I use .net core 3 preview)
The HttpContent class does not define a ReadAsAsync method. This is an extension method which I believe is considered obsolete,and is no longer supported. You may use ReadAsStringAsync(), ReadAsStreamAsync(), etc.
Example how to serialize the exception content to string:
var exception = await responseMessage.Content.ReadAsStringAsync();
The following code snippet demonstrates how to use HttpContent.ReadAsByteArrayAsync() to serialize the HTTP content to a byte array as an asynchronous operation, and then parse the array to a type you may define to hold the http error (something similar to the HttpError object, which you should not use, but simpler).
var responseBytes = await responseMessage.Content.ReadAsByteArrayAsync();
Note: Change the type specifier T to your custom type, or use built-in types such as string, according to your design...
JsonSerializer.Parse<T>(responseBytes, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
Hope this helps

How to mock httpclient

I am new in TDD can you please write test case using moq for following code -
public async Task<Model> GetAssetDeliveryRecordForId(string id)
{
var response = await client.GetAsync($"api/getdata?id={id}");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsAsync<Model>();
return result;
}
Thanks in advance.
You can use Moq to mock it.
Using Moq to do that for you
Now, you probably don’t want to create a new class for each response.
You could write a helper class for tests which you can prime with
whatever response you might want, but that is probably not flexible
enough.
Moq is a popular .NET library that helps mocking objects for testing.
In essence it uses reflection and expressions to dynamically generate
the mocks at runtime during your tests, based on specifications you
declare using a fluent API.
Now, there is a slight issue here, too. As you noticed, the SendAsync
method on the abstract HttpMessageHandler class is protected. The
caveat: Moq can’t auto-implement protected methods as easy as it does
with interfaces or public methods. Reason being, that the fluent API
uses expressions on the mocked Type, and this can’t offer private or
protected members, as you access the class from the outside here. So,
we have to use some more advanced features of Moq to mock out our
protected method here.
Moq, therefore, has an API for that. You do use Moq. Protected; in your
using clauses, and then you can go on on your Moq with the
.Protected() method. This gives you some additional methods on the
Moq, where you can access the protected members using their names.
A complete test of a class using a HttpClient using Moq would look
like this:
// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();
// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};
var subjectUnderTest = new MyTestClass(httpClient);
// ACT
var result = await subjectUnderTest
.GetSomethingRemoteAsync('api/test/whatever');
// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);
// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a GET request
&& req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);
For unit tests, you don’t mock HttpClient. Instead, you mock
HttpMessageHandler, put that into an HttpClient and have it return
whatever you want that way. If you don’t want to create s specific
derivation of HttpMessageHandler for each test, you can also have Moq
create the mocks for you automatically.
Read the whole article here.

Reading custom Content-Type (f.e. StackOverflow) feeds using HttpClient from WebAPI

I like a lot how the HttpClient is architectured - but I can't figure out how to add a "not quite standard" media type to be handled by the XmlSerializer.
This code:
var cli = new HttpClient();
cli
.GetAsync("http://stackoverflow.com/feeds/tag?tagnames=delphi&sort=newest")
.ContinueWith(task =>
{
task.Result.Content.ReadAsAsync<Feed>();
});
works fine when pointed to atom feeds that have Content-Type of "text/xml", but the one in the example fails with the "No 'MediaTypeFormatter' is available to read an object of type 'Feed' with the media type 'application/atom+xml'" message.
I tried different combinations of specifying MediaRangeMappings for the XmlMediaTypeFormatter (to be passed as an argument to ReadAsAsync) but with no success.
What is the "recommended" way to configure the HttpClient to map "application/atom+xml" and "application/rss+xml" to XmlSerializer?
Here is the code that works (credits to ASP.net forum thread):
public class AtomFormatter : XmlMediaTypeFormatter
{
public AtomFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/atom+xml"));
}
protected override bool CanReadType(Type type)
{
return base.CanReadType(type) || type == typeof(Feed);
}
}
var cli = new HttpClient();
cli
.GetAsync("http://stackoverflow.com/feeds/tag?tagnames=delphi&sort=newest")
.ContinueWith(task =>
{
task.Result.Content.ReadAsAsync<Feed>(new[] { new AtomFormatter });
});
Still, would like to see a solution without subclassing XmlMediaTypeFormatter - anybody?
The problem is that you are trying to convert the result straight to Feed. As error is clearly saying, it cannot figure our how to convert the application/atom+xml into Feed.
You would have to perhaps return as XML and then use and XmlReader to initialise your Feed.
Alternative is to provide your own media formatter - and implementation which encapsulates this.

Categories

Resources