Skip to content

Instantly share code, notes, and snippets.

@kparkov
Last active September 27, 2020 17:03
Show Gist options
  • Save kparkov/ef222ff4c59767078f35c3caf62a701f to your computer and use it in GitHub Desktop.
Save kparkov/ef222ff4c59767078f35c3caf62a701f to your computer and use it in GitHub Desktop.
Http Client unit testing

When unit testing C# ASP.NET controllers, the tests become rather artificial unless we make them as actual http(s) requests to the server.

This is a (stable) way to do so.

https://adamstorr.azurewebsites.net/blog/integration-testing-with-aspnetcore-3-1-testing-your-app

The core feature is the host built in TestHostBuilderFactory, and how it is instantiated in Tests. A client is then built from the IHost itself.

The HttpClientExtensions is just helper methods to make it easier to retrieve the payload from the response.

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace Client.Tests
{
public static class HttpClientExtensions
{
public static Task<(HttpStatusCode statusCode, T content)> GetAs<T>(this HttpClient client, string address) where T : class
{
if (client is null) throw new ArgumentNullException(nameof(client));
return client.Get<T>(address);
}
private static async Task<(HttpStatusCode statusCode, TReturn content)> Get<TReturn>(this HttpClient client, string address) where TReturn : class
{
if (client is null) throw new ArgumentNullException(nameof(client));
var response = await client.GetAsync(new Uri(address, UriKind.Relative));
var content = await response.Content.ReadAsStringAsync();
if (typeof(JArray).IsAssignableFrom(typeof(TReturn)) || typeof(JObject).IsAssignableFrom(typeof(TReturn)))
{
return (response.StatusCode, JsonConvert.DeserializeObject(content) as TReturn);
}
else
{
return (response.StatusCode, JsonConvert.DeserializeObject<TReturn>(content));
}
}
public static async Task<HttpStatusCode> Post(this HttpClient client, string address, object data)
{
if (client is null) throw new ArgumentNullException(nameof(client));
using var json = data.ToJson();
var response = await client.PostAsync(new Uri(address, UriKind.Relative), json);
return response.StatusCode;
}
public static async Task<(HttpStatusCode statusCode, TReturn content)> PostWithResult<TReturn>(this HttpClient client, string address, object data)
{
if (client is null) throw new ArgumentNullException(nameof(client));
using var json = data.ToJson();
var response = await client.PostAsync(new Uri(address, UriKind.Relative), json);
var body = await response.Content.ReadAsStringAsync();
var result = body.FromJson<TReturn>();
return (response.StatusCode, result);
}
public static async Task<HttpStatusCode> Put(this HttpClient client, string address, object data)
{
if (client is null) throw new ArgumentNullException(nameof(client));
using var json = data.ToJson();
var response = await client.PutAsync(new Uri(address, UriKind.Relative), json);
return response.StatusCode;
}
public static async Task<(HttpStatusCode statusCode, TReturn content)> PutWithResult<TReturn>(this HttpClient client, string address, object data)
{
if (client is null) throw new ArgumentNullException(nameof(client));
using var json = data.ToJson();
var response = await client.PutAsync(new Uri(address, UriKind.Relative), json);
var body = await response.Content.ReadAsStringAsync();
var result = body.FromJson<TReturn>();
return (response.StatusCode, result);
}
public static async Task<(HttpStatusCode statusCode, TReturn content)> PatchWithResult<TReturn>(this HttpClient client, string address, object data)
{
if (client is null) throw new ArgumentNullException(nameof(client));
using var json = data.ToJson();
var response = await client.PatchAsync(new Uri(address, UriKind.Relative), json);
var body = await response.Content.ReadAsStringAsync();
var result = body.FromJson<TReturn>();
return (response.StatusCode, result);
}
public static async Task<HttpStatusCode> Delete(this HttpClient client, string address)
{
if (client is null) throw new ArgumentNullException(nameof(client));
var response = await client.DeleteAsync(new Uri(address, UriKind.Relative));
return response.StatusCode;
}
public static StringContent ToJson(this object data)
{
string json = (data is string) ? (string) data : JsonConvert.SerializeObject(data);
return new StringContent(json, Encoding.UTF8, "application/json");
}
public static T FromJson<T>(this string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
}
}
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
namespace Client.Tests
{
public static class TestHostBuilderFactory
{
public static IHost StartHost()
{
var memoryConf = new Dictionary<string, string>
{
["CONNECTIONSTRING"] = GetRandomPgSqlConnectionString(),
};
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(memoryConf)
.Build();
var hostBuilder = Host
.CreateDefaultBuilder()
.ConfigureAppConfiguration(builder =>
{
builder.AddConfiguration(configuration);
})
.ConfigureWebHostDefaults(webHost =>
{
webHost.UseTestServer();
webHost.UseStartup<Startup>();
});
IHost host = hostBuilder.Start();
return host;
}
}
}
namespace Client.Tests
{
public class Tests
{
[Fact]
public async Task PostTest()
{
//Arrange
var viewmodel = new UpdateViewModel { Key = "Key", Value = "Value" };
using var host = UnitTestFactory.StartHost();
using var client = host.GetTestClient();
//Act
var response = await client.Post(address, viewmodel);
//Assert
response.Should().Be(HttpStatusCode.Created);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment