Unit tests for ASP.NET Core failing in VSTS pipeline due to HttpStatusCode.TemporaryRedirect - unit-testing

I have an ASP.NET Core 2.2 WebAPI that uses xUnit and FluentAssertions frameworks. It creates a Microsoft.AspNetCore.TestHost.TestServer and from that server object we create an HttpClient using Microsoft.AspNetCore.TestHost.TestServer.CreateClient() API.
The unit tests work great on my local Windows 10 machine using Visual Studio 2017 Pro. I committed the code, then did a pull request. The pull request automatically kicks off a build process to ensure it builds. Once it builds, it looks for then runs the unit tests. At this point it fails. I have 242 unit tests. All 242 unit test fail with the exact same error reported by the agent:
Expected resp.StatusCode to be OK, but found TemporaryRedirect.
All 242 unit tests make a request to the test server, so they all expect an HttpStatusCode.OK (or similarly expected) response. I should never expect a HttpStatuscode.TemporaryRedirect so I really don't want to add a test case for this.
The build server is running in VSTS as Microsoft Server 2012 R2 with Visual Studio 2017 Pro installed.
Why would the TestServer object return a Redirect ever?
If there is no way around this, can I force the HttpClient to auto-redirect when it receives this status so I only get the result of the API call?
Here is the code I am using to create the server and the client:
var config = new ConfigurationBuilder()
.SetBasePath(Path.GetFullPath("../../../../XXXXXXXXService/"))
.AddJsonFile("appsettings.json", optional: false)
.Build();
_server = new TestServer(new WebHostBuilder().UseStartup<Startup>().UseConfiguration(config));
TestHttpClient = _server.CreateClient();
An example unit test that is failing:
private string DeriveHttpPath(string path) =>
$"/{_rootPath.Trim('/')}/{path.TrimStart('/')}";
private static async Task<TResult> ValidateAndReadResponseAsync<TResult>(
HttpResponseMessage resp, HttpStatusCode statusShouldBe, Func<TResult> defFactory = null)
{
(resp.IsSuccessStatusCode ? HttpStatusCode.OK : resp.StatusCode).Should().Be(statusShouldBe);
try
{
return await resp.Content.ReadAsAsync<TResult>();
}
catch (Exception ex)
{
return defFactory != null ? defFactory.Invoke() : throw ex;
}
}
public async Task<TResult> GetDocumentDataAsync<TResult>(string documentId,
string path = null, HttpStatusCode statusShouldBe = HttpStatusCode.OK)
{
using (var msg = new HttpRequestMessage(HttpMethod.Get,
DeriveHttpPath($"{_collectionId}/{documentId}?path={path}")))
using (var resp = await _httpClient.SendAsync(msg))
{
return await ValidateAndReadResponseAsync<TResult>(resp, statusShouldBe);
}
}
_rootPath will be the root of the controller, i.e.: /api/MyController
Thank you for any help.

The issue I was having was because this was defined in the startup.cs:
#if !DEBUG
app.UseHttpsRedirection();
#endif
So when I was running the unit tests locally, it was compiled as Debug, where when it was pushed to the build server, it compiled as Release and the Https-Redirection was turned on.
I took that code out and made my WebAPI HTTPS-Only by setting it in Azure.

Related

Test runners inconsistent with HttpClient and Mocking HttpMessageRequest XUnit

So let me start by saying I've seen all the threads over the wars between creating a wrapper vs mocking the HttpMethodRequest. In the past, I've done the wrapper method with great success, but I thought I'd go down the path of Mocking the HttpMessageRequest.
For starters here is an example of the debate: Mocking HttpClient in unit tests. I want to add that's not what this is about.
What I've found is that I have tests upon tests that inject an HttpClient. I've been doing a lot of serverless aws lambdas, and the basic flow is like so:
//some pseudo code
public class Functions
{
public Functions(HttpClient client)
{
_httpClient = client;
}
public async Task<APIGatewayResponse> GetData(ApiGatewayRequest request, ILambdaContext context)
{
var result = await _client.Get("http://example.com");
return new APIGatewayResponse
{
StatusCode = result.StatusCode,
Body = await result.Content.ReadStringAsAsync()
};
}
}
...
[Fact]
public void ShouldDoCall()
{
var requestUri = new Uri("http://example.com");
var mockResponse = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(expectedResponse) };
var mockHandler = new Mock<HttpClientHandler>();
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
It.IsAny<HttpRequestMessage>(),
It.IsAny<CancellationToken>())
.ReturnsAsync(mockResponse);
var f = new Functions(new HttpClient(handler.Object);
var result = f.GetData().Result;
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get &&
req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);
Assert.Equal(200, result.StatusCode);
}
So here's where I have the problem!
When all my tests run in NCrunch they pass, and pass fast!
When I run them all manually with Resharper 2018, they fail.
Equally, when they get run within the CI/CD platform, which is a docker container with the net core 2.1 SDK on a Linux distro, they too fail.
These tests should not be run in parallel (read the tests default this way). I have about 30 tests around these methods combined, and each one randomly fails on the moq verify portion. Sometimes they pass, sometimes they fail. If I break down the tests per test class and on run the groups that way, instead of all in one, then these will all pass in chunks. I'll also add that I have even gone through trying to isolate the variables per test method to make sure there is no overlap.
So, I'm really lost with trying to handle this through here and make sure this is testable.
Are there different ways to approach the HttpClient where it can consistently pass?
After lots of back n forth. I found two of situations from this.
I couldn't get parallel processing disabled within the docker setup, which is where I thought the issue was (I even made it do thread sleep between tests to slow it down (It felt really icky to me)
I found that all the tests l locally ran through the test runners were telling me they passed when about 1/2 failed on the docker test runner. What ended up being the issue was a magic string area when seeing and getting environment variables.
Small caveat to call out, Amazon updated their .NET Core lambda tools to install via dotnet cli, so this was updated in our docker image.

Unit Testing Azure Functions with HttpRequestMessage

I'm running into a few issues when trying to unit test HTTP Trigger Azure Functions in Visual Studio. I've created a GitHub repo (https://github.com/ericdboyd/TestingAzureFunctions) with an example solution that contains both an Azure Function project and a Unit Test project that demonstrates the issues.
First, when I bring in Microsoft.AspNet.WebApi.Core it creates a conflict between System.Web.Http and Microsoft.AspNetCore.Mvc.WebApiCompatShim when trying to use IContentNegotiator. The only way around that was to alias WebApiCompatShim in the csproj file for the test project using the following:
<Target Name="ChangeAliasesOfStrongNameAssemblies" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'Microsoft.AspNetCore.Mvc.WebApiCompatShim'">
<Aliases>webapicompatshim</Aliases>
</ReferencePath>
</ItemGroup>
Once I got past that error, I run into this issue which I haven't been able to get past. Using the HttpRequestMessage.CreateResponse extension method to return a response in the Azure Function, I get "No service for type 'System.Net.Http.Formatting.IContentNegotiator' has been registered." when I try to test it. I have tried to build a HttpRequestMessage that I think should work with that extension method using the following code which can also be found in the GitHub repo, but it fails, and I have worked on trying to get past this for several hours now.
IServiceCollection services = new ServiceCollection();
services.AddOptions();
services.AddSingleton(typeof(IContentNegotiator), typeof(DefaultContentNegotiator));
IServiceProvider serviceProvider = services.BuildServiceProvider();
var httpContext = new DefaultHttpContext {RequestServices = serviceProvider};
var httpConfiguration = new HttpConfiguration();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(url),
Content = new StringContent(content, Encoding.UTF8, "application/json"),
Properties =
{
{ HttpPropertyKeys.HttpConfigurationKey, httpConfiguration },
{ nameof(HttpContext), httpContext}
}
};
If I don't use that CreateResponse extension method, and just create HttpResponseMessage objects, it works fine.
Just to set some additional context, I know this is not the best way to unit test the code being executed by the Azure Function. I'm unit testing that code much more granularly. But I want to be able to unit test the Azure Function that is performing the mapping between the http request and response to that logic.
There are two Azure Functions and two unit tests in the GitHub repo, one set with the extension method, one without to demonstrate the issue. But everything else is the same.
Not a direct answer to your problem, but it should help you go forward.
You are using Azure Functions v2 - .NET Standard version. This version is currently in beta, so it's a bit shady territory: the documentation is missing and some issues exist.
In V2 you are advised to use HttpRequest class and IActionResult instead of 'classic' HttpRequestMessage and HttpResponseMessage. The default template has a signature like this:
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req, TraceWriter log)
This should enable you to get rid of your shim and to unit test the functions similar to ASP.NET Core way.
Specify the jsonformatter while responding as below
req.CreateResponse(HttpStatusCode.OK, $"Hello, {name}", new JsonMediaTypeFormatter())

Unit Testing in Nancy causing Routebuilder exception using TinyIoc

Getting a System.MissingMethodException, Method not found: 'Void RouteBuilder.set_Item()
Get["/foo"] = parameters => { return Bar(Request);};
This runs fine when calling from browser, but fails when testing with this setup
var browser = new Browser(with =>
{
with.Module<Foobar>();
}
var response = brower.Get("/Foo", with => {with.HttpRequest();});
Any clue why the Routebuilder for testing won't pick up this route?
Turns out I had created the test project using the pre-release version of Nancy.Testing. This in turn made TinyIOC unhappy when trying to build routes/dependencies. So, if you see this mysterious message, check that your working code and test code are referencing the same packages.

Windows Store App Unit Testing a USB device

I'm writing a USB device API for Windows Store Apps that uses Windows.Devices.USB API from Windows 8.1 to connect and communicate with the custom USB device. I'm using the Visual Studio 2013 dev preview IDE.
The following function in the library is used to connect to the USB device.
(Simplified for clarity)
public static async Task<string> ConnectUSB()
{
string deviceId = string.Empty;
string result = UsbDevice.GetDeviceSelector(new Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"));
var myDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(result, null);
if (myDevices.Count > 0)
{
deviceId = myDevices[0].Id;
}
UsbDevice usbDevice = null;
try
{
usbDevice = await UsbDevice.FromIdAsync(deviceId);
}
catch (Exception)
{
throw;
}
if (usbDevice != null)
return "Connected";
return string.Empty;
}
When called from the Windows Store App project, this function connects to the device flawlessly. However, when called from the Unit Test Library for Windows Store Apps project, the statement in the try block throws an exception.
A method was called at an unexpected time. (Exception from HRESULT: 0x8000000E)
from what I've looked around, this happens when an Async function is called without the await keyword. But I'm using the await keyword alright!
Some more info, I am unable to use NUnit to write unit tests for Store Apps so am using the MSTest Framework.
[TestClass]
public class UnitTest1
{
[TestMethod]
public async Task TestMethod1()
{
await ConnectToUSB.ConnectUSB();
}
}
Also, I've included the following capability tags in the manifest files of both the App store projects too without which it's impossible for the Store Apps to connect to devices.
<m2:DeviceCapability Name="usb">
<m2:Device Id="vidpid:ZZZZ XXXX">
<m2:Function Type="name:vendorSpecific" />
</m2:Device>
</m2:DeviceCapability>
Is there something I'm missing or is this a bug in the MSTest Framework?
I think the problem is that
await UsbDevice.FromIdAsync(deviceId);
must be called on the UI thread because the app has to ask the user for access.
You have to CoreDispatcher.RunAsync to ensure you're on the UI thread or actually be in the code behind for a page.
I had the same problem with Unit Test App (Universal Windows) in VS 2017.
I verify answer of my predecessor Greg Gorman(see below). And I found this is true.
If you uses inside method body this construct:
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
async () =>
{
...
UsbDevice usbDevice = await UsbDevice.FromIdAsync(deviceId);
...
}).AsTask().Wait();
the FromIDAsync will work as you expect.
For your example change the test method to this:
[TestClass]
public class UnitTest1
{
[TestMethod]
public async Task TestMethod1()
{
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
async () =>
{
await ConnectToUSB.ConnectUSB();
}).AsTask().Wait();
}
}

Unit Tests Not Appearing

I'm using Visual Studio Express 2012 on the Windows 8 Release Preview and I can't seem to get my unit tests to appear in the test explorer.
I have a class called TestApp.Entity, and TestApp.EntityTest...
Here is my code:
namespace TestApp.Entity.Test
{
using System;
using System.Net.Http;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using TestApp.Domain;
[TestClass]
public class EntityTests
{
[TestMethod]
public async void TestObject1Deserialize()
{
Uri agencyUri = new Uri("*removed*");
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(agencyUri);
string responseBodyAsText = await response.Content.ReadAsStringAsync();
List<Agency> agencyList = Deserializers.AgencyDeserialize(responseBodyAsText);
CollectionAssert.Contains(agencyList, new Agency() { Tag = "*removed*", Title = "*removed*", ShortTitle = "", RegionTitle = "*removed*" });
}
}
}
I assume that's all I needed to do, but they still don't appear in the test explorer. Any advice would be helpful.
As per Stephen Cleary, "you need to make your unit tests async Task instead of async void for them to work correctly".
This fixed the problem and the tests appeared. It's odd that no errors appeared when I used void, but now I know. Thank you!
I have Visual Studio 2012 and i couldn't see the Tests in Test Explorer,
So I installed the following: NUnit Test Adapter
That fixed the issue for me !
Do a rebuild all on the application, including any projects that contain test classes and test methods. They should appear in Test Explorer soon after.