How to verify request body content in Flurl Http in unit tests? - c#

I'm using Flurl Http to make http requests. In the unit tests, I'm trying to verify that the expected content was passed to the sender. I'm trying it like:
httpTest.ShouldHaveCalled(url)
.WithVerb(HttpMethod.Post)
.WithContentType(contentType)
.With(w => w.Request.Content.ReadAsStringAsync().Result == content)
.Times(1);
However, this fails with System.ObjectDisposedException Cannot access a disposed object. Object name: 'System.Net.Http.StringContent'.
It looks like Flurl is disposing the request body content before the verification is done in the test. How can I capture the request body for verification?
EDIT (A fully reproducible example):
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Autofac.Extras.Moq;
using Flurl.Http;
using Flurl.Http.Testing;
using Xunit;
namespace XUnitTestProject1
{
public class MyClassTest : IDisposable
{
private readonly AutoMock container;
private readonly HttpTest client;
public MyClassTest()
{
this.container = AutoMock.GetLoose();
this.client = new HttpTest();
}
[Fact]
public async Task SendAsync_ValidateRequestBody()
{
const string url = "http://www.example.com";
const string content = "Hello, world";
var sut = this.container.Create<MyClass>();
await sut.SendAsync(url, content);
this.client.ShouldHaveCalled(url)
.With(w => w.Request.Content.ReadAsStringAsync().Result == content);
}
public void Dispose()
{
this.container?.Dispose();
this.client?.Dispose();
}
}
public class MyClass
{
public virtual async Task SendAsync(string url, string content)
{
await url.PostAsync(new StringContent(content, Encoding.UTF8, "text/plain"));
}
}
}

In most cases (see edit below), Flurl has captured it, you just have to access it differently.
In your example, w.Request is a "raw" HttpRequestMessage, from the HttpClient stack, that Flurl exposes so you can get under the hood if you need to. HttpRequestMessage.Content is a read-once, forward-only stream that has already been read and disposed by the time you're accessing it.
To assert the captured string body, you would typically just do this instead:
httpTest.ShouldHaveCalled(url)
...
.WithRequestBody(content)
EDIT
As you noted, this doesn't work based on how you're using Flurl. The string contained by StringContent is effectively write-only, i.e. no property exposes it for reading. This is the purpose of Flurl's CapturedStringContent. If you use that type as a direct replacement for StringContent, RequestBody will be available in your test.
The reason this isn't very well covered in the docs is because if you do things "the Flurl way", you're not explicitly creating content objects in the first place. PostStringAsync and PostJsonAsync are the far more common ways to send a POST request, and both are implemented using CapturedStringContent. Use one of those methods if you can, or use PostAsync(new CapturedStringContent(...)) if you need to get at the lower-level content object for some reason.

Related

Unit Testing nested methods

While I understand that when unit testing a method it is very important to mock all it's dependencies, what I still have confusion about is what happens when the method is nested? Do I mock only the dependencies of the parent method or do I mock the dependencies of the child method(s) as well or do I set expectations on the calls to the dependent object and set the exact return values so that I can perform the test that I want?
For instance, in the below example, if we want to unit test the method B, do we only mock IHttpClientFactory & ILogger or do we also set the method's return value to what we are actually expecting because otherwise when the test method executes it goes ahead and tries to execute methodC where it fails because the value of client after the line var client = _clientFactory.CreateClient() executes is null?
using System.Net.Http;
...
public class classA
{
private readonly IHttpClientFactory _clientFactory;
private sting url = "...";
private ILogger _log { get; set; }
...
public classA(ILogger log, IHttpClientFactory clientFactory, ...)
{
_log = log;
_clientFactory = clientFactory;
...
}
public string methodB(string inputB)
{
var varB = methodC(inputB);
...
return ..;
}
public string methodC(string inputC)
{
...
var client = _clientFactory.CreateClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage httpResponseMessage = await client.PostAsync(url, new StringContent(inputC, Encoding.UTF8, "application/json"));
responJsonText = await httpResponseMessage.Content.ReadAsStringAsync();
...
return ..;
}
}
So you have an HTTP client, a high-level method that gets you some structured data and a low-level method that gets you the contents of the response.
These things are more of an art than hard-cut rules, but the rule I prefer most of the time is to write code that can have all of its I/O abstracted and then mock or test-implement the I/O itself. This way the most amount of business logic would be testable.
I/O can be many things - file, network, user input, but even things such as getting a certificate from the cert store or reading registry setting. Any data that originates at runtime from outside the process is I/O, no matter the method.
When you mock functionality, the most common things you're interested in is validating the method's input or emulating its output (or both). So in your mock, you shouldn't be too concerned with the actual implementation since you're not testing your mocked method - you're testing whatever is calling it.
So... about your sample code. If you're trying to test MethodB, you'd need MethodC to have a test implementation - either by mocking the HttpClient it relies on or by making it virtual and having it overridden in test.
Side note: reuse the HttpClient, keep it with the class

Why can't I use HttpClient for Syncrhonous calls from ASP.Net?

I'm using a client library for accessing a 3rd party API. The library was generated by NSwagStudio from Swagger documentation.
The app I'm working on is entirely synchronous in all its calls and updating it to be async is out of scope of what I'm working on.
When I test the client library from a unit test, it works fine. When I try to call it from within an ASP.Net app, I get the following error:
The CancellationTokenSource has been disposed.
I've distilled the client library down to the essentials for demonstrating the problem, I selected an option to provide sync methods as well as async:
public class ClientApi
{
private readonly HttpClient _httpClient;
public ClientApi(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string BaseUrl { get; set; }
public object Get()
{
return Task.Run(async () => await GetAsync(CancellationToken.None)).GetAwaiter().GetResult();
}
/// <returns>OK</returns>
/// <param name="cancellationToken">
/// A cancellation token that can be used by other objects or threads to receive notice of
/// cancellation.
/// </param>
public async Task<string> GetAsync(CancellationToken cancellationToken)
{
var client_ = _httpClient;
try
{
using (var request_ = new HttpRequestMessage())
{
request_.Method = new HttpMethod("GET");
request_.RequestUri = new System.Uri(BaseUrl, System.UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(
request_,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
).ConfigureAwait(false);
try
{
// Exception occurs on following line
var responseData_ = response_.Content == null
? null
: await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
return responseData_;
}
finally
{
response_?.Dispose();
}
}
}
finally { }
}
}
Here's the code that calls it:
protected void OnClick(object sender, EventArgs e)
{
var httpClient = new HttpClient();
var client = new ClientApi(httpClient)
{
BaseUrl = "https://www.google.com"
};
var html = client.Get();
}
The code calling this is just an asp.net page with a button, and the button events runs the same code as the unit test that passes.
When I compare the runs in a debugger: from a unit test, the response_.Content object does not have a cancellation token, however when run from asp.net it does. In fact they almost seem to be different objects, despite the fact GetType() reports them both as being System.Net.Http.StreamContent. From decompiling the class, this doesn't have a _cancellationtoken property, so where is the debugger getting it from?
I'm guessing that the http request to my asp.net web app has it's own token and source, that is somehow getting used by the HttpClient. However, the client is awaiting all the async calls to get the result synchronously, so I don't understand how the underlying CTS could be disposed as we haven't returned from the call the client library yet.
Can anyone understand what's happening and is there a resolution?
First of, you should really rethink of rewriting your client app so you can implement async all the way.
“Async all the way” means that you shouldn’t mix synchronous and
asynchronous code without carefully considering the consequences. In
particular, it’s usually a bad idea to block on async code by calling
Task.Wait or Task.Result.
Taken from this great guide.
Basicaly, by running async code sync you will allways do things wrong.
But if you really need one solution, start by wrapping your disposable objects in using statements instead of manually disposing them.
Here's a simplified solutions of your ClientApi class which does what you need(But it can deadlock). The code is basically the same as in this answer.
public class ClientApi
{
public object Get(string url)
{
using (var client = new HttpClient())
{
var response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
return responseContent.ReadAsStringAsync().Result;
}
}
}
}
Read more about deadlock here

ServiceStack Performance

Let me start by saying I love the design of ServiceStack as a client. (I've never used it for server side)
I'm writing a C# wrapper for API calls and I keep getting timeout and authentication errors. I've contacted the developers at the other end and they assure me that there are no issues on their end and that I must be doing something wrong. Normally I wouldn't believe them and I'd build a sample project to demonstrate the issue but in this case they pointed me to a web page that will test the same API I'm running in C# and they can re-authenticate as fast as they can click the submit button. I forget the exact site they use for testing but enough of my story... I'm sure I'm doing something wrong I just don't know what.
Here's my Unit Test. If I run it by itself or with one copy it works fine (150-1100ms) but if I make 3 or more copies of it they I will get only 2-3 that pass and the rest will timeout.
[TestMethod]
[Timeout(5000)]
public void Login_Success1()
{
var client = new JsonServiceClient("apiurl");
var response = client.Login("XXXAccessKeyXXX", "XXXSecretKeyXXX");
//Assertions
}
This is my extension method:
public static class Extensions
{
public static (bool Success, string Message, string Token) Login(this JsonServiceClient client, string accessKey, string secretKey)
{
try
{
var response = client.Post(new LoginRequest(accessKey, secretKey));
var authorization = response.Headers.GetValues("Authorization")[0];
return (true, string.Empty, authorization);
}
catch (Exception ex)
{
return (false, $"Authentication failed: {ex.Message}", string.Empty);
}
}
}
And here's the login request:
[Route("/sessions")]
[DataContract]
internal class LoginRequest
{
internal LoginRequest(string accessKey, string secretKey)
{
AccessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey));
SecretKey = secretKey ?? throw new ArgumentNullException(nameof(secretKey));
}
[DataMember(Name = "accessKey")]
internal string AccessKey { get; set; }
[DataMember(Name = "secretKey")]
internal string SecretKey { get; set; }
}
I think this is all the relevant code but if you feel I missed something please lmk.
Your Request DTO's should implement either IReturn<T> or IReturnVoid otherwise if you're sending just an object you will call the deprecated Post() method:
/// <summary>
/// APIs returning HttpWebResponse must be explicitly Disposed, e.g using (var res = client.Post(url)) { ... }
/// </summary>
[Obsolete("Use: using (client.Post<HttpWebResponse>(requestDto) { }")]
public virtual HttpWebResponse Post(object requestDto)
{
return Send<HttpWebResponse>(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto);
}
Which because ServiceStack doesn't know how you want the Response deserialized it will return the open HttpWebResponse so you can inspect the Response yourself (as you're doing in your example). But this needs to be explicitly disposed as .NET's HttpWebRequest only allows a couple of concurrent requests open per domain which will cause your App to hang/timeout as it's waiting for Requests to be disposed to stay within the concurrent limit.
The preferred solution is to always annotate Request DTO's that you send with ServiceStack clients with a IReturn or a IReturn<T> interface marker, if it has none or you want to ignore the Response implement IReturnVoid otherwise implement IReturn<ResponseDtoType>:
class LoginRequest : IReturnVoid {}
Which instead calls the non-deprecated Post() method which disposes of the HttpWebResponse.
Otherwise if you want to send plain object DTO's you need to dispose of the HttpWebResponse after usage, e.g:
using (var response = client.Post<HttpWebResponse>(new LoginRequest(accessKey, secretKey)))
{
var authorization = response.Headers.GetValues("Authorization")[0];
}
API's which implicitly return HttpWebResponse were deprecated to avoid hard to identify issues like this, instead we recommend using the explicit API above which declares the HttpWebResponse return type at the call-site so it's easier to identify it needs to be disposed.
Also note the ServiceStack Service Clients are opinionated for calling ServiceStack Services, for calling other Services we recommend using HTTP Utils instead.

Good way to implment a .NET Service layer pattern with REST calls?

I'm trying to come up with a good way to have a service layer that is mainly making REST API calls to an external API. I've currently got a .NET Core 2.0 project where my services are being injected into my controllers, and I'm making calls that way. However, in my services themselves, I'm making calls to external APIs that require an access token. My current architecture has mostly been thrown together pretty quickly just to kind of "get things working", but now I was to decouple things a bit more and make it more testable. Here is an example of one of my service methods to illustrate where I'm at, an implementation of ISomeSearchService:
public async Task<SearchDataResponse> SearchAsync(string query, string accessToken)
{
SearchDataResponsedataResponse = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("www.somesite.com/api");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
IList<KeyValuePair<string, object>> parameters = new List<KeyValuePair<string, object>>()
{
KeyValuePair.Create<string, object>( "searchTerm", query ),
};
var response = await client.GetAsync("/search" + UriFormatter.AsQueryString(parameters));
if(response.IsSuccessStatusCode)
{
string responseBody = await response.Content.ReadAsStringAsync();
dataResponse = JsonConvert.DeserializeObject<SearchDataResponse>(responseBody);
}
}
return dataResponse;
}
I know this is riddled with issues. Namely one of the biggest to me is that is constructing this HTTP client in the service itself. I would like to abstract that out, and maybe pass in a client to the service that is already constructed some way. That way in my tests, I can pass in mock clients to be able to test these methods without having to make actual HTTP calls. I'm not sure if there is a better way to handle that though. I can't find much guidance on service layers that are making HTTP calls. Most documentation I find is related to calling a DB directly.
Another issue is that I don't like passing the access token directly into the service. Since I was limited on time, I just did that to get things working, but I'm not happy with it.
Does anyone have some experience with this or a design that I could look into that would decouple this out more?
The goal is to inject an HttpClient instance. You can extract an interface from HttpClient to assist with mocking.
public interface IHttpClient : IDisposable
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
}
You will need an implementation that constructs instances of HttpClient but you can also use the interface to mock the request.
Now your service codes against the IHttpClient
public class SearchService : ISomeSearchService
{
private readonly IHttpClient httpClient;
public SearchService(IHttpClient httpClient)
{
this.httpClient = httpClient;
}
}
Alter your request so that you send an HttpRequestMessage instead of using .GetAsync() that way you can alter the Authorization header per request.
public async Task<SearchDataResponse> SearchAsync(string query, string accessToken)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri)
{
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
var response = await httpClient.SendAsync(request);
...
}
}
Then register the singleton HttpClient
services.AddSingleton<IHttpClient, HttpClientFactory>();
services.AddScoped<ISomeSearchService, SearchService>();

ASP.NET - GET Web Request API

I'm new to APIs, so I've been trying to use a web request to GET information from Reddit, since the API is unlocked. I've been able to use the right URL to get information using a REST client extension, but I want to implement the body of the data and simply just print it out to a web page.
I know this is easier with python, but they use C#/ASP.NET at my work. I've looked off of tutorials such as:
http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client
I was only able to obtain a header when I used this tutorial and my code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace API_TESTING
{
class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
class Program
{
static void Main()
{
RunAsync().Wait();
}
static async Task RunAsync()
{
using(var client = new HttpClient())
{
//TODO - send HTTP requests
client.BaseAddress = new Uri("http://www.reddit.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//-----
HttpResponseMessage response = await client.GetAsync("r/todayilearned/new.json");
if(response.IsSuccessStatusCode)
{
Console.Write(response);
}
}
}
}
}
Can someone explain how to use ASP.NET for API calls? or link to other up-to-date tutorials? Thank you.
You're almost there. After you get the response, you need to read its content.
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
}
You might want to check out the RedditSharp project to see how others have done this.
Btw, the tutorial you linked to does almost exactly what I answered with under the Getting a Resource (HTTP GET) section. Only difference is they used the generic ReadAsAsync while you can use ReadAsStringAsync if you're just writing the body to the console.

Categories