How can I unit test delegating handler - c#

I have an httpClient which has timeout,retry,authorization delegating handlers.
var authorizationHandler = new AuthorizationDelegatingHandler();
var retryHandler = new RetryPolicyDelegatingHandler(3);
var timeoutHandler = new TimeOutDelegatingHandler(TimeSpan.FromSeconds(5));
authorizationHandler.InnerHandler = retryHandler;
retryHandler.InnerHandler = timeoutHandler;
_myHttpClient = new HttpClient(authorizationHandler);
I am following along a tutorial and I am trying to unit test my timeout handler
[TestMethod]
[ExpectedException(typeof(TimeoutException))]
public async Task GetDocuments_Timeout_MustThrowTimeoutException()
{
var unauthorizedResponseHttpMessageHandlerMock = new Mock<HttpMessageHandler>();
unauthorizedResponseHttpMessageHandlerMock.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).ReturnsAsync(new HttpResponseMessage()
{
StatusCode = System.Net.HttpStatusCode.Unauthorized
});
var httpClient = new HttpClient(unauthorizedResponseHttpMessageHandlerMock.Object);
httpClient.BaseAddress = new Uri("http://localhost");
var timeoutHandler = new TimeOutDelegatingHandler(TimeSpan.FromSeconds(3));
var testableClass = new MyCustomclass(httpClient);
var cancellationSource = new CancellationTokenSource();
await testableClass.Foo();
}
I am stuck at this point. How can i chain those handlers in unit test project?

The answer is very simple you should not. What you should do is:
Unit test the units. That is a simple piece of code, design to do a single or few things.
Create tests for each handler individually.
Creat tests for the handler construction that occurs during configuration.
You should not test how the chains work as unit tests, as this broadens the scope too much and renders the unit tests unmaintainable.
That's what integration testing is for.

Related

Testing Azure Function - how to create fake HTTP request that includes an item in HttpContext

We have an azure functions project and it looks like the previous dev used a similar approach as described here for unit testing:
http://dontcodetired.com/blog/post/Mocking-HttpRequest-Body-Content-When-Testing-Azure-Function-HTTP-Trigger-Functions
It works well, except that I now need to inject a fake value for the the following string:
HttpContext.Items["MS_AzureFunctionsRequestID"]
cus at some point in the code, we do this:
req.HttpContext.Items["MS_AzureFunctionsRequestID"].ToString()
And right now, the unit test is failing because the fake request object doesn't include this field.
I've tried a few different things but so far, no dice.
Assuming I'm using the following code, where can I inject this fake string:
private static Mock<HttpRequest> CreateMockRequest(object body)
{
var ms = new MemoryStream();
var sw = new StreamWriter(ms);
var json = JsonConvert.SerializeObject(body);
sw.Write(json);
sw.Flush();
ms.Position = 0;
var mockRequest = new Mock<HttpRequest>();
mockRequest.Setup(x => x.Body).Returns(ms);
return mockRequest;
}
I've tried to add an additional "sw.Write()" line and play around with the contents but I can't seem to get it working.
Thanks.
EDIT 1
So I changed the above method so it has one additional line:
private static Mock<HttpRequest> CreateMockRequest(object body)
{
var ms = new MemoryStream();
var sw = new StreamWriter(ms);
var json = JsonConvert.SerializeObject(body);
sw.Write(json);
sw.Flush();
ms.Position = 0;
var mockRequest = new Mock<HttpRequest>();
mockRequest.Setup(x => x.Body).Returns(ms);
mockRequest.Setup(x =>x.HttpContext.Items.Add("MS_AzureFunctionsRequestID", REQID));
return mockRequest;
}
REQID is a new private const that has been defined in the same class as the CreateMockRequest() method.
This is the method that ultimately calls this code:
[Fact]
public async void post_request_should_be_stored_in_queue_and_storage_table(){
var messageQueue = TestFactory.CreateAzureStorageQueue();
var request = TestFactory.CreateWidgetHTTPRequest();
Console.Write(request.HttpContext.Items["MS_AzureFunctionsRequestID"].ToString());
var storageTable = TestFactory.SaveToStorageTable();
var widgetRequest = new WidgetRequest(storageTable);
var response = (OkObjectResult)await widgetRequest.CreateWidget(request, messageQueue, logger);
Assert.NotNull(response);
Assert.True(((AzureStorageQueueTestClient<string>)messageQueue).Messages.Count > 0);
}
When it hits the console.write - the field is null.
Can I call setup on the mock object twice?
how do I set x.body and x.HttpContext?
EDIT 2
I found this post: How to mock HttpContext.Current.Items with NUnit and Rhino Mocks
I'm trying to use the answer to adapt my code, but when I try to create the httpContext object, i'm getting a CS0144 error
"Cannot create an instance of the abstract type or interface 'HttpContext'
"Cannot create an instance of the abstract type or interface 'HttpResponse'
The code I'm playing around with so far just has this:
[Fact]
public void can_create_http_context()
{
const string REQUEST_GUID_FIELD_NAME = "RequestGUID";
var httpContext = new HttpContext(new HttpRequest("", "http://google.com", ""), new HttpResponse(new StringWriter()));
HttpContext.Current = httpContext;
}

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);
}

How to mock ConfigurationManager.ConnectionStrings with moq in Unit Test case project?

I am writing a unit test cases fro Azure function.In my function app configuration section i have added my connection string.
When the azure function runs in azure below line of code is working fine.
var connStr = ConfigurationManager.ConnectionStrings["SqlConnection"].ConnectionString;
If i am calling the azure function from unit test case this line is throwing exception.
"object reference not set to an instance of an object".
Form unit test case how i can execute this line of code.(using Moq).
Test Method :
[TestMethod]
public async Task GetDataFromAzureSQL_Test()
{
var req = new HttpRequestMessage();
var obj = new Modeltest
{
id = 1,
name ="",
location=""
};
var content = new StringContent(JsonConvert.SerializeObject(obj).ToString(), Encoding.UTF8, "application/json");
req.Content = content;
var result = await Function.Function1.Run(req, new Mock<ILogger>().Object);
Assert.IsTrue("OK" == result.StatusCode.ToString());
}

How do I unit test requests against SSL-only Web Api Controller?

I have a unit test which uses the OWIN TestServer class to host my Web Api ApiController classes for testing.
I first wrote the unit test when the REST API did not have the HTTPS (SSL) requirement baked into the Controller itself.
My unit test looked something like this:
[TestMethod]
[TestCategory("Unit")]
public async Task Test_MyMethod()
{
using (var server = TestServer.Create<TestStartup>())
{
//Arrange
var jsonBody = new JsonMyRequestObject();
var request = server.CreateRequest("/api/v1/MyMethod")
.And(x => x.Method = HttpMethod.Post)
.And(x => x.Content = new StringContent(JsonConvert.SerializeObject(jsonBody), Encoding.UTF8, "application/json"));
//Act
var response = await request.PostAsync();
var jsonResponse =
JsonConvert.DeserializeObject<JsonMyResponseObject>(await response.Content.ReadAsStringAsync());
//Assert
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Now that I've applied the attribute to enforce HTTPS, my unit test fails.
How do I fix my test so that, all things being equal, the test passes again?
To fix this unit test, you need to change the base address for the TestServer.
Once the server has been created set the BaseAddress property on the created object to use an "https" address. Remember the default BaseAddress value is http://localhost.
In which case, you can use https://localhost.
The changed unit test would look as follows:
[TestMethod]
[TestCategory("Unit")]
public async Task Test_MyMethod()
{
using (var server = TestServer.Create<TestStartup>())
{
//Arrange
server.BaseAddress = new Uri("https://localhost");
var jsonBody = new JsonMyRequestObject();
var request = server.CreateRequest("/api/v1/MyMethod")
.And(x => x.Method = HttpMethod.Post)
.And(x => x.Content = new StringContent(JsonConvert.SerializeObject(jsonBody), Encoding.UTF8, "application/json"));
//Act
var response = await request.PostAsync();
var jsonResponse =
JsonConvert.DeserializeObject<JsonMyResponseObject>(await response.Content.ReadAsStringAsync());
//Assert
Assert.IsTrue(response.IsSuccessStatusCode);
}
}

How do I make a unit test to test a method that checks request headers?

I am very, very new to unit testing and am trying to write a test for a pretty simple method:
public class myClass : RequireHttpsAttribute
{
public override void OnAuthorization(AuthoizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
var header = Convert.ToBoolean(request.Headers["Special-Header-Name"]);
if (!(header || request.IsSecureConnection))
{
HandleNonHttpsRequest(filterContext);
}
}
}
This method, which inherits from the RequireHttpsAttribute, checks if a certain header is present from a page, if it's missing or false, and the page is not secure, then it will call HandleNonHttpsRequest, otherwise it does nothing.
We are using Moq and Nunit for testing. I have found some resources to help build a fakeHttpContext with Moq, but honestly I'm not sure how to use it or where to go within my unit tests to ensure that fake HttpContexts are or are not causing the HandleNonHttpsRequest method to call.
I really appreciate any guidance with this issue.
// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
{ "Special-Header-Name", "false" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://www.example.com"));
request.Setup(x => x.RawUrl).Returns("/home/index");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();
var actionDescriptor = new Mock<ActionDescriptor>();
var controllerContext = new ControllerContext(context.Object, new RouteData(), controller.Object);
var filterContext = new AuthorizationContext(controllerContext, actionDescriptor.Object);
var sut = new myClass();
// act
sut.OnAuthorization(filterContext);
// assert
Assert.IsInstanceOfType(filterContext.Result, typeof(RedirectResult));
var redirectResult = (RedirectResult)filterContext.Result;
Assert.AreEqual("https://www.example.com/home/index", redirectResult.Url);
Yes, I'd use Moq and create a Mock<AuthorizationContext>. You'll need a series of mock objects to setup the fake request, most notably to specify a NameValueCollection of fake headers.
var request = new Mock<HttpRequestBase>();
request.SetupGet(c => c.Headers).Return(new NameValueCollection{ /* initialize values here */});
request.SetupGet(c => c.IsSecureConnection).Return(/*specify true or false depending on your test */);
var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Return(request.Object);
var filterContext = new Mock<AuthorizationContext>();
filterContext.SetupGet(c => c.HttpContext).Return(httpContext.Object);
var myclass = new myClass();
myClass.OnAuthorization(filterContext.Object);
(sorry if syntax or usage is slightly off; doing this from the top of my head)
You may need to go in and mock any additional members on filterContext that HandleNonHttpsRequest invokes. I have two recommendations for going about this, as it can sometimes be a hassle if the method you are testing is doing lots of complex stuff on filterContext: 1) check visually and, if it's straight forward enough, mock all the invoked pieces 2) create the myClass.OnAuthorizationRequest, but don't implement any code yet other than the call to HandleNonHttpsRequest. Keep running the test and fixing missing/incorrectly mocked members until the test passes. Then implement your actual logic for OnAuthorizationRequest, testing and fixing (rinse repeat) until it passes.
I encountered an issue with the accepted solution using ASP.NET MVC 4. To resolve it I mocked the http context Items attribute otherwise the sut.OnAuthorization was causing an object is undefined exception:
MockHttpContext.Setup(x => x.Items)
.Returns(new System.Collections.Generic.Dictionary<object, object>());

Categories

Resources