1

I'm still trying to add in some unit tests for an ASP.NET site (not MVC). One of the methods I need to test makes use of the HttpRequest Request object, Request.Path to be exact. I'm trying to write the tests using Visual Studio 2008's built in testing framework. Whenever the test executes the method in question, I receive a System.Web.HttpExecption: Request is not Available in this context. I understand why it is not available (there is no running web server and not path supplied), but how can I proceed with testing the method?

Since everyone likes to see code, here's the code in question:

protected string PageName
{
    get 
    {
        return Path.GetFileName(Request.Path).Substring(0, Path.GetFileName(Request.Path).Length - 5); 
    }
}

protected Change SetupApproval(string changeDescription)
{
    Change change = Change.GetInstance();
    change.Description = changeDescription;
    change.DateOfChange = DateTime.Now;
    change.Page = PageName;
    return change;
}

Here's the test:

[TestMethod]
public void SetupApproval_SubmitChange_ValidateDescription()
{
    var page = new DerivedFromInternalAppsBasePage_ForTestingOnly();
    var messageToTest = "This is a test description";
    var change = page.SetupApproval(messageToTest);
    Assert.IsTrue(messageToTest == change.Description);
}

In addition, I've read through Microsoft's documentation here: http://msdn.microsoft.com/en-us/library/ms182526(v=vs.90).aspx and tried using the [HostType("ASP.NET")], [UrlToTest(http://localhost:port/pathToAspxPage.aspx")], and [AspNetDevelopmentServer("C:\PathToDllAssembly", "NotSureParameter")] Attributes they suggest, but no luck. (As you can see, I'm not sure about what I should be using for a few of the parameters.).

Lastly, I tried Phil Haack's TestWebServer http://haacked.com/archive/2006/12/12/Using_WebServer.WebDev_For_Unit_Tests.aspx and read through Scott Hanselman's post http://www.hanselman.com/blog/NUnitUnitTestingOfASPNETPagesBaseClassesControlsAndOtherWidgetryUsingCassiniASPNETWebMatrixVisualStudioWebDeveloper.aspx For Phil's server, I'm not sure what I would use for parameters in the ExtractResource method.

1
  • 1
    "Conventional unit testing wisdom" (j/k) would suggest removing that dependency from the code and allow it to be passed in or managed from a mock object. Commented May 18, 2012 at 21:08

4 Answers 4

3

I've been struggling with similar problems recently and what was really helpful is the Moles framework from Microsoft Research - http://research.microsoft.com/en-us/projects/moles/. It allows you to fake anything from BCL including HttpContext.Current.

Sign up to request clarification or add additional context in comments.

1 Comment

I think there's a better way to solve this problem (see below). TypeMock Isolator could also be used in this case.
2
+50

There is a very similar problem to the one you have encountered which is described in the book "Working Effectively with Legacy Code" by Michael Feathers. In particular, the refactoring is called "Adapt Parameter".

The "problem" with your code is that it is directly coupled to the HttpRequest, specifically Request.Path. So the overall approach is you want to decouple your code from HttpRequest.

Similarly to what is suggested above, here is another way to do the decoupling following the idea in Michael Feather's book. I haven't tried compiling this, so please excuse any typos or syntax errors.

public interface ParameterSource
{
    public string Path {get; }
}

public class FakeParameterSource : ParameterSource
{
   public string Value;
   public string Path { get { return Value } }
}

public class RealParameterSource : ParameterSource
{
   private HttpRequest request;
   public RealParameterSource(HttpRequest aRequest)
   {
     request = aRequest;
   }
   public string Path { get { return request.Path } }
}

Now here's the important part of what you need to change (and here's one way of doing it):

// small rename
protected string GetPageName(ParameterSource source) 
{ 
   return Path.GetFileName(source.Path).Substring(0, Path.GetFileName(source.Path).Length - 5);
}

Above the injection happens at the method level. You could do it via a constructor or a property too.

Your test code could now be something like:

protected Change SetupApproval(string changeDescription) 
{
    ParameterSource p = new FakeParameterSource { Value = "mypath" }; 
    Change change = Change.GetInstance(); 
    change.Description = changeDescription; 
    change.DateOfChange = DateTime.Now; 
    change.Page = GetPageName(p); //  now uses the value in the parameter source
    return change; 
} 

I hope you get the idea and find this useful

Comments

0

The key to testing methods that use HttpContext is to use HttpContextBase. HttpContextBase is an abstract class which makes it easily mockable. Unfortunately HttpContext does not implement HttpContextBase so you need to use HttpContextWrapper to wrap the real HttpContext to HttpContextBase. This article has a good and simple explanation: http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext

Your question doesn't say where the example code is located. If it's all in the code-behind you are going to have a difficult time trying to test that. Instead of having logic in code-behind, you should look into Model-View-Presenter(MVP) -pattern. There are frameworks for the MVP, but it is also quite easy to do it yourself.

In MVP presenters contain all the logic and pages code-behind only implements View-interface containing properties which can be bound to the UI. All dependencies can be injected to presenter making it easy to test.

Comments

0

I agree with @Alex Taylor (sorry, I can't comment): the best way of unit testing a (non-MVC) web app is to make sure the bits don't rely on the whole environment being present; that's why it's called unit testing, after all. So you could replace PageName with this:

protected string PageName
{
    get 
    {
        return GetPageName(Request.Path);
    }
}

protected static string GetPageName(string path)
{
    return Path.GetFileNameWithoutExtension(path);
}

And with this you can unit test GetPageName, and be confident that PageName works as expected.

Remember, there's no point trying to unit test Request.Path, because that has an expected behaviour (or 'contract') that it should fulfil. If you're not sure what Request.Path will return in different circumstances, you should check the documentation instead of adding unit tests.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.