Unit Test MVC with ASP.NET Dev Server - c#

I want to confirm that my "HomeController" class is being selected by the route I've created. So I have a test like this:
[TestMethod]
[UrlToTest("http://localhost:14478/home")]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("$(SolutionDir)\\MvcBuildApp")]
public void MyTest()
{
System.Diagnostics.Debugger.Break();
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
MvcApplication.RegisterGlobalFilters(GlobalFilters.Filters);
//This fetches DefaultControllerFactory (for now, anyway...)
var factory = ControllerBuilder.Current.GetControllerFactory();
//mock a context...
var httpContext = CreateHttpContext("http://localhost:14478/home", "GET");
RouteData route = routes.GetRouteData(httpContext);
var ctrlInstance = factory.CreateController(new RequestContext(httpContext, route), (string)route.Values["controller"]);
//ASSERT the instance is of type "HomeController"
//...
}
It fails, saying that 'http://localhost:14478/home' completed successfully without running the test.
I noticed that in the VS output window, there is also this message No connection could be made because the target machine actively refused it 127.0.0.1:14478. I figured Cassini must not be active. So I chose to launch the site being tested (ctrl+F5) before launching the unit test. It then changed the VS output to this:
WebTestAdapter.ConnectToHostAdapter: unexpected exception occured.
Microsoft.VisualStudio.TestTools.HostAdapters.AbortTestExecutionException:
Error in the application. at
Microsoft.VisualStudio.TestTools.HostAdapters.WebTestAdapter.ConnectToHostAdapter()
To try to resolve this, I've followed the advice of these articles:
Debug while running a test in ASP.NET solution
Unit Tests for ASP.NET web services: this discusses the "completed successfully without running a test" error.
Configuring ASP.NET Unit Tests: I got the $(SolutionDir) idea from this article.
...but I still get the error no matter what I do. Suggestions?
UPDATE/CLARIFICATION
This question is not about testing MVC routes. The purpose of this question is to discover how to make ASP.NET MVC properly initialized to allow more "in depth" automated testing. I have chosen a "route testing" scenario, against the DefaultControllerFactory, merely as an example. In this example, the DefaultControllerFactory does not behave properly unless ASP.NET MVC is properly initialized.

This is a common requirement when testing MVC applications, and lucky you, there is a framework that will simplify your life when testing MVC controllers:
The tools is:
http://mvccontrib.codeplex.com/
And as an example you can create tests with just one line:
"~/".ShouldMapTo<HomeController>(controller => controller.Index());
As a reference:
http://geekswithblogs.net/thomasweller/archive/2009/11/02/unit-testing-asp.net-mvc-routes.aspx
http://www.arrangeactassert.com/how-to-unit-test-asp-net-mvc-controllers/

You may be interested in reviewing Steve Sandersons MVC testing framework. If you download the source you will have a great example on how to initialize the MVC framwork:
http://blog.codeville.net/2009/06/11/integration-testing-your-aspnet-mvc-application/

Related

C# integration test status "not run" when authorization returns forbidden

I am currently writing C# integration tests for an ASP.NET Core project. I want to have tests that validate endpoint security - specifically, that the endpoint grants access to an authorised user, but denies access to an unauthorised user.
I've got a working test for the first use case, but I've run into some odd behaviour on the second.
Here is the integration test:
[Test]
public async Task GetDrives_ShouldReturnForbidden_WhenCalledByExternalUser()
{
// Arrange
TestClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test", "a#b.c");
UriBuilder builder = new("https://localhost/api/Drive");
builder.Query = "Id=49";
// Act
HttpResponseMessage response = await TestClient.GetAsync(builder.Uri);
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseContent);
// Assert
response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
responseContent.ShouldBeEmpty();
}
Now, the test does exactly what its name says - the endpoint returns forbidden. Where the behaviour gets odd is that when I run this test, I would expect it to pass, but instead, it results in a "not run" status. When debugging the test, I can see that it runs all the way through, so the test does run, but the final status doesn't reflect that.
After much googling, I haven't found anything that explains this behaviour. My suspicion is that this is in part because the request is processed by an [Authorize] attribute, so it never hits the endpoint code, which the test framework knows, thus deeming the test "not run". Has anyone encountered this before and can confirm that this is what's happening? And does anyone know how to make this test produce a pass/fail result?
Troubleshooting a similar testing issue (a test that wasn't being consistently rerun) helped me discover the answer.
The cause of both issues was an exception being thrown by the console.writeline, but the IDE wasn't actively flagging it, only logging it, therefore somewhat obscuring the issue from view.
The writeline value parameter was only whitespace when the endpoint call was forbidden, causing a System.ArgumentException: The parameter cannot be null or empty. A small tweak to the code prevents the exception from occurring and provides a correct pass result.
Here was the code before:
And here was the code afterwards:
The takeaway here - if running tests in visual studio, and you're getting odd behaviour like tests that run but don't provide an outcome, or tests that ran once but don't on a rerun, check the test runner output - there may be errors hiding away there!

Kentico v8.2.12: Why is my WebAPI Route not registered correctly (usually only on the first release)?

Summary
A Kentico 8.2 website fo which I have recently implemented a Web API service isn't registering routes on first deployment and all calls return 404. Further redeployments usually fix the issue, but I would like to fix it permanently before it is released to PROD.
What is preventing the first deployment from registering the route properly?
Background
We have a Kentico v8.2.12 website that uses Web Forms using .NET Framework v4. I have registered a Web API Controller, but it appears on the first release the route isn't registered and any calls to the service returns "404 (Not Found)".
When I first deployed to the DEV environment the Web API route wasn't registered, but upon deploying another build it magically worked. One or two other releases into the DEV environment caused similar issues, but in these instances re-deploying the same build worked.
The same issue has now occurred when released to UAT, however as the deployments are carried out by another team it will be more time-consuming to re-deploy builds and looks unprofessional. I am also wary of this occurring in PROD---which may cause the live website to be down further than necessary.
Web API Implementation
The Web API Controller is inside the CMS Website project and not a separate library.
Global.asax.cs
The Global.asax.cs file's Application_Start() method registers the route and looks similar to the below:
protected void Application_Start()
{
// Scripts Bundling here, which havs been removed for brevity
BundleTable.EnableOptimizations = false;
// Registering the API
RouteTable.Routes.MapHttpRoute(
name: "DefaultApiWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
}
MyController.cs
My Controller looks similar to the below stored under the CMS website: CMSApp/ApiControllers/MyController.cs
[assembly: RegisterApiController(typeof(CMSApp.ApiControllers.MyController))]
namespace CMSApp.ApiControllers
{
public class MyController : ApiController
{
Channel Channel = new Channel();
[HttpPost]
public int Create()
{
Response objResponse = Channel.Instance.DoSomething();
HandleResponse(objResponse);
return objResponse.SessionHandle;
}
}
}
In the webbrowser, accessing /api/my/create returns a 404 (Not Found), but I expect it to tell me it's a POST method.
Lib versions [Edit, I have since updated the libs but issue still prevails]
Microsoft.AspNet.WebApi : v4.0.30506
Microsoft.AspNet.WebApi.Client : v4.0.30506
Microsoft.AspNet.WebApi.Core : v4.0.30506
Microsoft.AspNet.WebApi.WebHost : v4.0.30506
Question?
Why do the first deployments into an environment not work, but most further deployments work as I expect them to?
The issue was due to ASP.NET caching.
Once "MS-ApiControllerTypeCache.xml" was removed under "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files" and IIS was restarted, the controller was picked up.

How to do integration tests on an mvc controller that calls httpcontext.current

I need to do integration tests on an ASP.Net MVC controller that calls HttpContext.Current in each of it's methods. I have tried various approaches and done plenty of research already, although I feel like there is something i am missing. I have gone the routes of:
Asp.Net core: I had found a hack on stack overflow using asp.net core test host courtesy of Stefan Steiger (How to get HttpContext.Current in ASP.NET Core?). This is a great solution. However, upon an attempt at implementation and further reading, it appears that this will only work with an aspnetcore controller and is not backward compatible. I am aware of a shim however I cannot find any instruction on how to use it.
Spinning up iis express using a batch file and making the calls. The issue i got with this approach is that i need to be able to debug in the tests. I tried attaching the visual studio debugger however this was for some reason not working. I kept getting the message on the breakpoint "The breakpoint will not currently be hit. No symbols have been loaded for this document".
I've also researched how to launch the project with the test controller programmatically but could not find how to launch a visual studio web project programmatically. (looked in to launching it via a process and msbuild)
Currently i am testing using owin self host, however, the HttpContext.Current is null in the controller (obviously). If there is a way around this, it is the most preferable method.
Any advice on how to get these tests done would be greatly appreciated.
Thanks,
Edit, I am attaching using the following method:
private static bool AttachToIIS(int tries = 10, int threadsleep = 500)
{
System.Threading.Thread.Sleep(1000);
bool isAttached = false;
int count = 0;
while (isAttached == false && count < tries)
{
_DTE dte = (_DTE)Marshal.GetActiveObject("VisualStudio.DTE.14.0");
Processes processes = dte.ActiveWindow.DTE.Debugger.LocalProcesses;
foreach (EnvDTE.Process process in processes)
{
System.Diagnostics.Debug.WriteLine("process: " + process.Name);
try
{
if (process.Name.Contains("iis"))
{
process.Attach();
isAttached = true;
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
System.Threading.Thread.Sleep(threadsleep);
count++;
}
return isAttached;
}
The one oddity is that when i attach to the debugger (I am no COM expert), it attaches the debugger of the visual studio instance that was opened first.
Depending on why you use HttpContext.Current, you could modify your code to use the HttpContext property of the Controller class instead.
Then you can create a custom ControllerContext for your tests that can contain an HTTP context as well. Note that MVC is more intelligent than the old ASP.NET because it uses HttpContextBase which you can inherit and even mock a completely custom HTTP context in your tests.
Relevant links:
https://www.danylkoweb.com/Blog/how-to-successfully-mock-httpcontext-BT
ASP/NET MVC: Test Controllers w/Sessions? Mocking?
How to mock the Request on Controller in ASP.Net MVC?
ASP.NET MVC unit test controller with HttpContext

C# MVC : Can you make a webservice that only takes POST actions

I was wondering if you can make a MVC webservice that only has HTTP POST methods.
For instance:
I use Selenium to open a page : http://localhost/main.html from .EXE application.
then my MVC app looks like this:
[HttpGet]
public ActionResult main()
{
return View();
}
[HttpPost]
public ActionResult Final(USER user)
{
saveUSER(user);
return null;
}
I just don't want MVC to open another page that the one that selenium used. I used to do this with an HttpWebRequest to the GET method, but even if I returned null, it would open a blank page AND then work out the rest of the logic.
The better way to test Post methods, services and repositories in a MVC Application is using functional or unit tests. There are nice frameworks that would help you to develop these tests.
I've been using Nunit to develop automated test in my work and in my own projects, the framework provide me the tools to develop functional and unit tests and runs all selenium integration tests inside him.

Integration testing ASP.NET Web API 2 using HttpServer/HttpClient; no HttpContext

I'm looking to integration test a web service built on ASP.NET Web API 2. Many things such as cookies, getting the current principal, etc. are done through HttpContext.Current.
I found the following resource on integration testing ASP.NET:
http://amy.palamounta.in/blog/2013/08/04/integration-testing-for-asp-dot-net-web-api/
This works great. It spins up an in-memory host and combined with the automated schema generation of Entity Framework 6, setup and teardown are easy.
The problem comes in when things try and use HttpContext.Current to, as aforementioned, get cookies/etc. It seems that when hosting in-memory using HttpServer, HttpContext.Current is always null. This does make a little sense given there's no real request, but is a pain - it means the integration tests can't cover anything requiring anything from this property.
What can I do here? It looks like very little of the data I'm using is present anywhere but HttpContext.Current, so am I just going to have to spin up full external instances on IIS?
In order to test your api using self host you have to avoid calling HttpContext.Current from a web api context. You can wrap it in something you can mock, like so:
public static class CurrentHttpContext
{
public static Func<HttpContextBase> Instance = () => new HttpContextWrapper(HttpContext.Current);
}
Then when you are creating your test server you could set the CurrentHttpContext to some fake HttpContextBase. For example using FakeItEasy (http://fakeiteasy.github.io/):
var context = A.Fake<HttpContextBase>();
CurrentHttpContext.Instance = () => context;
And then use this anywhere you need to access HttpContext:
var context = CurrentHttpContext.Instance();
// do what you need to do to httpcontext now like setting principal, cookies etc

Categories