Recently, I came across a situation where my WebMethod was accessing the Session object. This particular WebMethod was used extensively by the application so I decided to write unit test for it. Here is a simple example of a webmethod.
[WebMethod(EnableSession=true)]
public int foo()
{
int userId = Convert.ToInt32(HttpContext.Current.Session["UserId"]);
return userId;
}
First of all I don't really think that using HttpContext inside a WebMethod is a good idea. One of the main purpose of using web service is to distribute the application in multi-tier and to provide independent consumable services. In this situation the access to a session will fail since they belong to different virtual directories.
Writing unit tests for these web methods is hard and time consuming. So, I thought of refactoring the method and came up with the following:
[WebMethod(EnableSession=true)]
public int foo(int userId)
{
return userId;
}
Now, the foo method takes the userId as a parameter instead of extracting it from the session object.
Let's take a different scenario where you just want to unit test the session object. This time you can mock the session. Take a look at the code below:
[Test]
public void should_be_able_to_mock_session_object()
{
string page = "";
string vDir = "/VirtualRoomWebApps";
string phyDir = @"C:\Projects\VirtualRoom\VirtualRoomSolution\VirtualRoomWebApps\";
TextWriter tw = new StringWriter();
HttpWorkerRequest worker = new SimpleWorkerRequest(vDir, phyDir, "VirtualRoomService.asmx", String.Empty, tw);
HttpContext context = new HttpContext(worker);
IHttpSessionState sessionState = new SimpleSessionState();
Mockery mockery = new Mockery();
var mock = mockery.NewMock<IHttpSessionState>();
Expect.Once.On(mock).Method("Add");
mock.Add("something", "somevalue");
mockery.VerifyAllExpectationsHaveBeenMet();
}
SimpleSessionState inherits from the IHttpSessionState interface. The implementation is shown below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.SessionState;
namespace TestSuite.Helpers
{
public class SimpleSessionState : IHttpSessionState
{
private Dictionary<String, Object> _container = new Dictionary<string,object>();
public void Add(string name, object value)
{
_container.Add(name, value);
}
public object this[string name]
{
get
{
return _container[name];
}
set
{
_container[name] = value;
}
}
}
}
The lesson learned is "If you cannot easily write unit test for a method then refactor or redesign the method".