How to write unit test for HttpContext.Current.Request.Headers - asp.net-mvc-3

Below is an Example.
public class MyController : Controller
{
[Route("~/api/mycontroller")]
[HttpGet]
public int ID()
{
try
{
return somecontroller.getID(ID);
}
catch (Exception ex)
{
throw ex;
}
}
}
Above it the controller that is fetching the ID from the below controller.
Below is the controller that it is inherited.
public class Controller : ApiController
{
public int ID
{
get
{
return int.Parse(HttpContext.Current.Request.Headers["ID"]);
}
}
}
How do i write unit test case for the following.???

Oh, unit testing HttpContext.Current. That's one of my favorites :-)
You can't write a unit test for something that depends on HttpContext.Current. So if you want to write testable code the first step is to search in your entire solution for the HttpContext.Current keyword and simply wipe them out from existence.
In this particular case you would of course replace them with the corresponding abstraction:
public class Controller : ApiController
{
public int ID
{
get
{
return int.Parse(Request.Headers.GetValues("ID").FirstOrDefault());
}
}
}
Now it's completely trivial to unit test your Web API controller properly:
// arrange
var sut = new MyController();
sut.Request = new HttpRequestMessage();
sut.Request.Headers.TryAddWithoutValidation("ID", "5");
// act
var actual = sut.SomeControllerAction();
// assert
Assert.AreEqual(5, actual);

Related

Setting [BindNever] during the action execution filter flow

Does anyone know how I can mark an argument on ActionDescriptor.Parameters to behave in a similar way the [BindNever] is behaving?
I want to always exclude a specific argument from a specific type without keep decorating it on the Controller.
Essentially I would like to be able to add my injected to my functions somehow how similar to the way its done with CancellationToken
public class TestController : ControllerBase
{
[HttpGet(Name = "Get")]
public IActionResult Get([BindNever] IInjectedInterface injected)
{
//Injected can be used in this method
return Ok();
}
[HttpPost(Name = "Post")]
public IActionResult Post([BindNever] IInjectedInterface injected, FormModel formModel)
{
//Injected doesn't work here. There is an error that
/*System.InvalidOperationException: 'Action 'WebApplication3.Controllers.TestController.Post (WebApplication3)'
has more than one parameter that was specified or inferred as bound from request body. Only one parameter per action may be bound from body.
Inspect the following parameters, and use 'FromQueryAttribute' to specify bound from query, 'FromRouteAttribute' to specify bound from route,
and 'FromBodyAttribute' for parameters to be bound from body:
IInjectedInterface injected
FormModel formModel'
*/
return Ok();
}
}
public class ActionExecutionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var injectedParam = context.ActionDescriptor.Parameters.SingleOrDefault(x => x.ParameterType == typeof(IInjectedInterface));
if (injectedParam != null)
{
context.ActionArguments[injectedParam.Name] = new Injected(99);
}
await next.Invoke();
}
private class Injected : IInjectedInterface
{
public Injected(int someData)
{
SomeData = someData;
}
public int SomeData { get; }
}
}
I was able to solve it. Apparently you need to add the following lines on your program.cs to avoid the model binder related errors.
options.ModelMetadataDetailsProviders.Add(
new ExcludeBindingMetadataProvider(typeof(IInjectedInterface)));
options.ModelMetadataDetailsProviders.Add(
new BindingSourceMetadataProvider(typeof(IInjectedInterface), BindingSource.Special));

How to specify response type in ASP.NET Core middleware

My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}

How to write test code for Web API in Visual Studio?

I'm a bit new to test project. I currently have a web api project which contains Get, Put, Post and Delete methods. When comes to writing test cases, I'm confused. Should I write test code to test the Http URL?
My web api code:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
using (var unitOfWork = new UnitOfWork(_db))
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
}
}
And in my test project, I kind of stuck here. We are using Xunit. How to write test code to test the Get method? Or should I write code to test the URL api/values/5 instead, but how?
[Fact]
public void GetTest()
{
using (var unitOfWork = new UnitOfWork(new MockDatabase()))
{
}
}
Any help would be appreciated.
You need to make a couple of changes before you can really unit test your controller. You need to pass an instance of your UnitOfWork class into the controller in its constructor. Then your controller method code becomes:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
Then in your unit tests you do this:
[Fact]
public void GetTest()
{
// Arrange
// You really want to mock your unit of work so you can determine
// what you are going to send back
var unitOfWork = new MockUnitOfWork();
var systemUnderTest = new Controller(unitOfWork);
system.Request = new HttpRequestMessage();
// Act
var result = systemUnderTest.Get(1);
// Assert
// Here you need to verify that you got back the expected result
}
Injecting the UnitOfWork class into the controller is probably another question. Mark Seemann has an excellent post on the subject, but it might be a little advanced. There are a number of different ways to accomplish that with simpler (but maybe not as robust methods). Google is your friend with that. But if you have questions, post another question.
Hope that helps.
You would need to make some design changes to your controller to make it easy to test. In your action you are creating an instances which will make it difficult to test with a fake dependencies to the controller. Also your controller should depend on abstractions rather than concretions which will allow the controller to be more testable.
public class MyWebApiController : ApiController {
private IUnitOfWork unitOfWork;
public MyWebApiController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id) {
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null) {
return NotFound();
} else {
return Ok(result);
}
}
//...other code
}
Notice the controller uses dependency injection to inject an IUnitOfWork. That makes the controller more testable, because you can inject mocks of its dependencies when unit testing.
From there it is just to create an instance of the controller and call the method under test with mocks of the dependencies.
[Fact]
public void GetTest() {
//Arrange (Setup the parts needed to run test)
var unitOfWork = new MockUnitOfWork(new MockDatabase());
//Or using your mocking framework of choice
//var unitOfWork = Mock.Of<IUnitOfWork>(); //this is using Moq
var controller = new MyWebApiController(unitOfWork);
var id = "Test Id value here";
//Act (call the method under test)
var result - controller.Get(id);
//Assert (check results)
//...Do your assertion pertaining to result of calling method under test
}
Reference : Unit Testing Controllers in ASP.NET Web API 2

Adding custom data for an operation to Application Insights telemetry

I'm trying to add a bunch of custom data fields to every piece of telemetry I can, and this data is consistent across a single operation, but varies from operation to operation.
I have a custom ITelemetryInitializer, and within that I can do something like:
public class MyInitializer : ITelemetryInitializer
{
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = "PC";
}
}
But I don't understand how I'm suppose to push this data into this initializer.
I've added something like this:
public class MyInitializer : ITelemetryInitializer
{
private string mPlatform = "unknown";
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = mPlatform;
}
public void SetPlatform(string platform)
{
mPlatform = platform
}
}
And then at the controller level I do something like this:
foreach (var init in TelemetryConfiguration.Active.TelemetryInitializers)
{
var customInit = init as MyInitializer;
if (customInit != null)
{
customInit.SetPlatform(requestPlatform);
}
}
But this is horribly clunky, and prone to error (e.g. if a piece of telemetry gets sent before this function is called), and I'm not really sure if this is thread-safe.
What's the intended way of passing around this kind of data?
I think I've solved this now, the solution is to write to the properties of the TelemetryClient within the controller like this:
[Route( "[controller]" )]
public class MyController : Controller
{
private readonly TelemetryClient mTelemetryClient;
public MyController(
TelemetryClient TelemetryClientArg )
{
mTelemetryClient = TelemetryClientArg;
mTelemetryClient.Context.Properties.Remove("platform");
}
[HttpPost]
[Produces( "application/json" )]
public IActionResult Post( [FromBody] RequestClass RequestData )
{
mTelemetryClient.TrackTrace("Test trace 1"); // doesn't have platform set
mTelemetryClient.Context.Properties["platform"] = RequestData.platform;
mTelemetryClient.TrackTrace("Test trace 2"); // has platform set correctly
}
}
This seems to be safe as the controller constructor appears to be called before each http request is processed and the context within the TelemetryClient is unique per thread. I would like to get confirmation from the team that this is reasonable.

Moles appears to require moled methods to be hashed

I am trying out Moles on a home project to (hopefully) be able to recommend that it be adopted in projects at work. I am working with VS 10.0.30319 and Moles 1.0.0.0.
I have created the following class:
public class DeveloperTestControlBL
{
public static bool VerifyCurrentDevelopmentStatus(int tpid, int testStatusID)
{
return false; // For this example, always return false,
// so that a return of true means the delegation worked
}
}
In my test class, I have created a test which tests a class which calls the VerifyCurrentDevelopmentStatus method. I would have expected to have a declaration of the form:
[TestMethod]
[HostType("Moles")]
public void TestPreTCIToEditReady_WithMoles()
{
var devTCBL = new SDeveloperTestControlBL();
devTCBL.VerifyCurrentDevelopmentStatus = delegate(int tpid, int testStatusID)
{
return true;
}
// rest of the test here
}
What I have found is that to set the delegate I have to do this:
[TestClass]
public class MyTest
{
[TestMethod]
[HostType("Moles")]
public void TestPreTCIToEditReady_WithMoles()
{
DelegateExample.Moles.MDeveloperTestControlBL.VerifyCurrentDevelopmentStatusInt32Int32 =
VerifyCurrentDevelopmentStatusAlwaysTrue;
// Rest of the test here
}
private bool VerifyCurrentDevelopmentStatusAlwaysTrue(int tpid, int status)
{
return true;
}
Does anyone have any advice as to what I am doing incorrectly? I have the
using Microsoft.Moles.Framework; statement and have a reference to DeveloperTestControlBL.Moles in the test project.
Thanks in advance,
Ron L
Try this
devTCBL.VerifyCurrentDevelopmentStatus (delegate(int tpid, int testStatusID)
{
return true;
});
It worked for me.

Resources