Skip to main content

.NET Core HttpClient–Testing redirections

When testing a specific API I had to check if the user was redirected to the correct location. However although I thought that I had written my api correctly, the response code didn’t match.

[ApiController]
[Route("job")]
public class JobController : ControllerBase
{
[HttpGet("{jobId}")]
public IActionResult Job(Guid jobId)
{
//Return OK if job is still running/scheduled
var job=_jobRepository.Get(jobId);
if (job == null)
return NotFound(jobId);
if (job.Status == Status.Pending)
return Ok();
//Return 500 if job failed
if (job.Status == Status.Failed)
{
return StatusCode(500);
}
//Return 302 when job is finished
if (job.Status == Status.Succeeded)
{
return RedirectToAction(nameof(JobOutPut), new { jobId });
}
return NotFound(jobId);
}
}

Here is the related test code:

internal static class HttpClientExtensions
{
internal static async Task PollForResults(this HttpClient httpClient,Uri requestUri, Func<HttpResponseMessage, Task> successAction, Func<HttpResponseMessage, Task> failureAction = null)
{
int numberOfAttempts = 3;
int attemptCount = 0;
//Keep querying the URI until we get a 302 with a result
while (attemptCount< numberOfAttempts)
{
var pollingResponse = await httpClient.GetAsync(requestUri);
if (pollingResponse.StatusCode == System.Net.HttpStatusCode.OK)
{
attemptCount++;
await Task.Delay(1000);
}
if (pollingResponse.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
if (pollingResponse.Content.Headers.ContentType.Equals(new MediaTypeHeaderValue("application/zip")))
{
if (failureAction == null)
return;
await failureAction(pollingResponse);
}
break;
}
if (pollingResponse.StatusCode == System.Net.HttpStatusCode.Redirect)
{
// Response should contain a location header with job output URI
var jobOutputLocation = pollingResponse.Headers.Location;
var outputResponse = await httpClient.GetAsync(jobOutputLocation);
if (successAction == null)
return;
await successAction(outputResponse);
break;
}
}
}
public class JobApiTests : IClassFixture<SelfHostedApi>
{
public HttpClient Sut { get; }
public GenerationApiTests(SelfHostedApi fixture)
{
Sut = fixture.CreateClient();
}
[Fact]
public async Task API_Enables_Bulk_Processing_Files_On_URL_Path()
{
//Arrange
var templateFile = new Uri("http://localhost/ExampleTemplate.docx");
var xmlFile = "ExampleXML.xml";
var targetFile = $"ExampleDocument-{Guid.NewGuid()}.zip";
var form = new MultipartFormBuilder()
.AddFile(xmlFile, "xmlFiles", "File1.xml")
.AddFile(xmlFile, "xmlFiles", "File2.xml")
.AddFile(xmlFile, "xmlFiles", "File3.xml")
.Build();
//Act
var response = await Sut.PostAsync($"/job/bulk/uri?template={templateFile}", form);
response.EnsureSuccessStatusCode();
//Response should contain a location header with job id
var jobLocation = response.Headers.Location;
await Sut.PollForResults(jobLocation, successAction: response => response.WriteToFile(targetFile,checkStatusCode:false));
//Assert
Assert.True(File.Exists(targetFile));
Assert.True(new FileInfo(targetFile).Length > 0);
}
}
view raw JobApiTests.cs hosted with ❤ by GitHub

Do you spot my mistake? Let’s have a look at the documentation:

//
// Summary:
// Creates an instance of System.Net.Http.HttpClient that automatically follows
// redirects and handles cookies.
//
// Returns:
// The System.Net.Http.HttpClient.
public HttpClient CreateClient()
{
return CreateClient(ClientOptions);
}
view raw CreateClient.cs hosted with ❤ by GitHub

The default HttpClient will automatically handle redirects what makes it impossible to check for the 302 status code

To change this behavior, you need to create your own HttpHandler and use it when building up your HttpClient instance:

public class JobnApiTests : IClassFixture<SelfHostedApi>
{
public HttpClient Sut { get; }
public GenerationApiTests(SelfHostedApi fixture)
{
var handler = new HttpHandler();
Sut = fixture.CreateDefaultClient(handler);
}
internal class HttpHandler : DelegatingHandler
{
public HttpHandler()
{
//We override the default handler to avoid automatic redirection
//This makes it hard to detect the job completion in our tests
this.InnerHandler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
}
}
}

Popular posts from this blog

Kubernetes–Limit your environmental impact

Reducing the carbon footprint and CO2 emission of our (cloud) workloads, is a responsibility of all of us. If you are running a Kubernetes cluster, have a look at Kube-Green . kube-green is a simple Kubernetes operator that automatically shuts down (some of) your pods when you don't need them. A single pod produces about 11 Kg CO2eq per year( here the calculation). Reason enough to give it a try! Installing kube-green in your cluster The easiest way to install the operator in your cluster is through kubectl. We first need to install a cert-manager: kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml Remark: Wait a minute before you continue as it can take some time before the cert-manager is up & running inside your cluster. Now we can install the kube-green operator: kubectl apply -f https://github.com/kube-green/kube-green/releases/latest/download/kube-green.yaml Now in the namespace where we want t...

Azure DevOps/ GitHub emoji

I’m really bad at remembering emoji’s. So here is cheat sheet with all emoji’s that can be used in tools that support the github emoji markdown markup: All credits go to rcaviers who created this list.

DevToys–A swiss army knife for developers

As a developer there are a lot of small tasks you need to do as part of your coding, debugging and testing activities.  DevToys is an offline windows app that tries to help you with these tasks. Instead of using different websites you get a fully offline experience offering help for a large list of tasks. Many tools are available. Here is the current list: Converters JSON <> YAML Timestamp Number Base Cron Parser Encoders / Decoders HTML URL Base64 Text & Image GZip JWT Decoder Formatters JSON SQL XML Generators Hash (MD5, SHA1, SHA256, SHA512) UUID 1 and 4 Lorem Ipsum Checksum Text Escape / Unescape Inspector & Case Converter Regex Tester Text Comparer XML Validator Markdown Preview Graphic Col...