Viser opslag med etiketten asp.net mvc. Vis alle opslag
Viser opslag med etiketten asp.net mvc. Vis alle opslag

onsdag den 9. oktober 2013

Mocking session state in a asp.net mvc4 unit test using Moq


I recently spent more time than I'd liked to figure out how to mock session state within an ASP.NET MVC test project. So here's the solution to that one, so hopefully you won't spend as much time as I.

Important disclaimer: I was all over the dial to search for info, but most of what I dug up was related to asp.net mvc 3. This below solution works for me with ASP.NET MVC 4 and Moq as mocking framework.

Consider the following controller and single action-method:


public class HomeController : Controller
{
public HomeController()
{
// default constructor, usually you'd inject some repository or what not here, but for this example we'll keep it simple
}

public ViewResult TestMe()
{
System.Diagnostics.Debug.WriteLine(Session["selectedMonth"]);
System.Diagnostics.Debug.WriteLine(Session["selectedYear"]);
return View();
}
}


The action-method references the current httpcontext's Sesson collection. So if we instantiate a 'HomeController' and try to obtain a ViewResult when calling the action-method, our unit test will fail with a NullReferenceException.

So, since the action-method wants to know about the Session collection, which resides in the HttpContext for the request to the method, we need to provide a HttpContext object. The below unit test code creates a mock HttpContext object, hooks it up to a mock Session object and passes it the instantation of the controller - and the test will then pass, as now the action-method has a HttpContext to reach into and yank out the Session info.


[TestMethod]
public void TestActionMethod()
{
// create the mock http context
var fakeHttpContext = new Mock();

// create a mock session object and hook it up to the mock http context
var sessionMock = new HttpSessionMock {{"selectedYear", 2013}, {"selectedMonth", 10}};
var mockSessionState = new Mock();
fakeHttpContext.Setup(ctx => ctx.Session).Returns(sessionMock);

// ... and here's how to attach a http context identity, just in case you'll come to need that, too
var fakeIdentity = new GenericIdentity("mno@ucsj.dk");
var principal = new GenericPrincipal(fakeIdentity, null);
fakeHttpContext.Setup(t => t.User).Returns(principal);

// we'll need to hook our http context up to a controller context mock - because we can't provide our controller with the http context mock directly
var homeControllerMock = new Mock();
homeControllerMock.Setup(foo => foo.HttpContext).Returns(fakeHttpContext.Object);

// all set up, now we'll instantiate the controller and pass our controller context object into its 'controllerContext' property
var target = new HomeController()
{
ControllerContext = homeControllerMock.Object
};

// ... and the below call to the action method won't throw a nullReferenceException, because now it has a Session state to dig into
ViewResult result = target.TestMe();

// ... and so the test will pass
Assert.AreEqual(string.empty, result.ViewName);
}

mandag den 23. september 2013

Contact web api from InfoPath Designer 2013

I was having some trouble getting values from my home-brewed web api into a InfoPath Designer 2013 form, so I wanted to document the solution for those of you out there perhaps struggling with this as well.

I was calling a simple 'Hello World'-web api that would return a string. My simple aip looks like this:


namespace ADlookup.Controllers
{
/* model */
public class employee
{
public string employeeId { get; set; }
public string bossId { get; set; }

}


public class ADLookupController : ApiController
{
[HttpGet]
public employee getNearestBossId([FromUri] string id)
{
return new employee()
{
employeeId = id,
bossId = id
};

}
}
}


However, upon calling this api in InfoPath Designer 2013, I got the following error message:



... to indicate the xml structure is invalid.

I didn't get it - when I called the service directly from my browser, valid xml was returned!:



The problem, as it turned out, was of course the one that as opposed to the xml displayed by my browsers, the InfoPath Designer application - correctly - retrieved the data by way of json, the web api's default MO.

So, in order to serve up only XML, for InfoPath to agree with, we'll strip away the web api's possibility of serving up json altogether. Modify your WebApiConfig to resemble the below, though of course take into consideration the routes you have for your own app:


public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

// tbe below must be enabled if this rest service is used with infopath designer 2013 - it only accepts xml data.
// thus we'll remove the json return type
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.JsonFormatter);

}
}


Now, as we re-publish the web api service, we can try once more the url in InfoPath, which will now allow us to proceed to the next step:


Intriguingly, on a completely different note, the 'get data from http rest service' workflow component in Sharepoint Designer 2013 works off JSON, as opposed to XML! But that's a blog-entry for another rainy day.

Hope this helps someone, anyone,

thanks for reading.