Skip to content
September 11, 2011 / pauldundon

Accessing HTTPContext in ASP.NET MVC Test Projects

The Problem: You have a Visual Studio Test Project as part of an MVC solution. Your code relies on HTTPContext (eg, uses the Session or Application objects), but this is not available using the default test context.

The Solution: Several steps are needed:

1. If you are using the ASP.NET development server, change your project properties so that it uses a fixed port number. You must be able to specify a stable URL for your application.

2. Add a file called default.aspx to your MVC solution. This provides an entry point for the test host. If you attempt to use an MVC URL as your entry point, you will receive an error like the following:

The Web request ‘http://localhost:22307/Home/Index’ completed successfully without running the test. This can occur when configuring the Web application for testing fails (an ASP.NET server error occurs when processing the request), or when no ASP.NET page is executed (the URL may point to an HTML page, a Web service, or a directory listing). Running tests in ASP.NET requires the URL to resolve to an ASP.NET page and for the page to execute properly up to the Load event. The response from the request is stored in the file ‘WebRequestResponse_Obfuscate.html’ with the test results; typically this file can be opened with a Web browser to view its contents.

3. Add a test settings file to your solution. Do this by right-clicking on the solution in solution explorer and choosing Add New Item…

4. Double click on the settings file to edit it, and select Hosts. Select ASP.NET as the host type. Enter the full URL of the newly-added default.aspx file as the URL to test and enter the path to the folder holding your MVC project

Other considerations:-

1. You can add an additional test settings file configured to use the default host if you have tests that can run without HTTPContext, and you wish to avoid the overhead of using the ASP.NET host. You can use “Select Active Test Settings” from the Test menu to switch between them.

2. The debugger will not stop at breakpoints in your code under this configuration. You can trigger a break by adding Debug.Assert(False) to your code. When the assertion fails you will see a dialog; selecting “Retry” will prompt you to open a debugger. A similar effect can be attained using Debugger.Break(), but this will cause windows to report that the development server has stopped working.

3. In some circumstances (particularly when breaking in the debugger) the test host will fail to clean up after itself, so that it will refuse to begin subsequent test runs because it detects a previous test run in progress. The error is:

The test adapter (‘Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestAdapter, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’) required to execute this test could not be loaded. Check that the test adapter is installed properly. The ASP.NET Web application at is already configured for testing by another test run. Only one test run at a time can run tests in ASP.NET. If there are no other test runs using this Web application, ensure that the Web.config file does not contain an httpModule named HostAdapter.

In this case, edit the web.config for your MVC solution and remove the two keys beginning “microsoft.visualstudio.teamsystems” under appSettings, as well as the two references to “HostAdapter”.

July 25, 2011 / pauldundon

LinkButtons in MVC / Razor

The problem: You want the effect of a Web Forms LinkButton in ASP.NET MVC. For example, you may wish to allow the user to take multiple actions on a single form, or have a form submission triggered by a link rather than a button.

Possible Approach: It is possible to use Javascript to change the action attribute on a form, and to use an anchor tag to cause a form submission. So, an anchor tag could modify the action on its parent form to a given controller action and submit the form, thus providing LinkButton-like behaviour.

Possible Solution: Create an extension method for the Html object:

Imports System.Runtime.CompilerServices

Public Module ExtendHTML
    <Extension()>
    Public Function LinkButton(html As HtmlHelper, linkText As String, actionName As String) As MvcHtmlString
        Return html.ActionLink(linkText, actionName, Nothing, New With {.class = "linkButton"})
    End Function
End Module

Include the following Javascript in your page

$(function () {
    $(".linkButton").click(
    function (event) {
        event.preventDefault();
        var link = event.target;
        var action = link.href;
        while (link.tagName != 'FORM') {
            link = link.parentElement;
            if (!link) {
                return;
            }
        }
        link.action = action;
        link.method="POST"
        link.submit();
    });
});

In your .vbhtml file, import your project namespace:

@Imports MyProject

Then use the LinkButton function as follows:

@Html.LinkButton("Click here", "MyAction")

Further Improvements: It would be possible to provide multiple overloads for the LinkButton method to provide support for a wider range of options, generally echoing the overloads of ActionLink.

July 18, 2011 / pauldundon

Unexpected GET Requests

The problem: You have a website which works fine in a test environment but experiences unexpected GET requests when running in a production environment. These requests may be causing problems in your application if, for example, you are using IsPostback to determine whether or not to initialise session state.

The cause: The HTTP specification recommends that GET requests are safe, ie, do not cause changes of state on the server. Specifically, the specification requires that clients can assume that GET requests are safe and that they are not responsible for any side-effects. In some network architectures, intermediaries (eg caching proxies) will make independent GET requests as part of their operation. For example, a client might request index.aspx and an intermediary make the same request a few seconds later. Importantly, the intermediary may use the same session identifier (cookie or URL) as the client.

Another possible cause will show a problem if you are using Chrome or Safari but not IE or Firefox. If your HTML contains an <img> tag with an empty src attribute, IE and FF will do nothing, but Chrome and Safari will resolve the source URL to the URL of the page hosting the image. This will generate a GET request for the page.

The solution: Although it isn’t practical to screen out requests of this kind, it is generally possible and desirable to make your handling of GET requests safe. For example, consider moving session state to view state or initialising it the first time it is read.

June 11, 2011 / pauldundon

Creating Curried Javascript Functions in a Loop

The problem: You want to create a series of curried functions (for example, as event handlers for an array of elements) and attempt to do so using a loop, as follows:

function foo() {
	for (i = 0; i < elements.length; ++i) {
		var element = elements[i];
		var otherData = someArray[i]
		$addHandler(item, "click", function () { doSomething(otherData); });
	}
}

However, clicking on the items (in this example) produces the same result for every item, specifically, each item is associated with the last value in otherData referenced by the loop.

The cause: Javascript is evaluating otherData at the point of invocation rather than at the point of constructing the anonymous function (as VB would). As a consequence, it always takes its most recent value.

The solution: Create your curried function within another function. This creates an independent scope for each instance of the currying parameters:

function foo() {
	for (i = 0; i < elements.length; ++i) {
		var element = elements[i];
		var otherData = someArray[i]
		$addHandler(item, "click", createHandler(otherData));
	}
}

function createHandler(param) {
	return function () { doSomething(param); }
}
May 31, 2011 / pauldundon

Using Selenium with Firefox 4 and Microsoft AJAX

Selenium provides a framework for browser-based testing which can be integrated with unit test frameworks such as NUnit or unit test projects in Visual Studio. Part of the Selenium product set is the Selenium IDE, which allows you to record the actions required for a test using Firefox, and produce C# code to reproduce them in your unit tests. Although using the IDE seems to be the less preferred approach to using Selenium, it is still supported.

The Selenium IDE is not flagged as compatible with FF4, but does work with it. To use it, first install the Firefox Add-on Compatibility Reporter to disable add-on version checking.

Next install the IDE add-on.

To launch the IDE, select Selenium IDE from the Tools menu in Firefox (if the tools menu isn’t visible, hit the Alt key). You’re now ready to start recording the actions for your test. You can copy this to the clipboard as a C# code fragment.

To make use of this, you will need to download the .NET client files for Selenium. Unzip the download and create a project with references to the dlls inside. You can now incorporate the C# code generated by the IDE into this project. You will need the following imports:

using OpenQA.Selenium.Firefox;
using OpenQA.Selenium;
using Selenium;

The generated code relies on a local variable with the name selenium; the following code will provide this:

// You may use any WebDriver implementation. Firefox is used here as an example
IWebDriver driver = new FirefoxDriver();
// A "base url", used by selenium to resolve relative URLs
String baseUrl = "http://localhost"
// Create the Selenium implementation
ISelenium selenium = new WebDriverBackedSelenium(driver, baseUrl);

If your website uses Microsoft AJAX, you will need to add code to pause the progress of your test during asynchronous updates (Selenium will automatically insert code to wait for full page GETs or POSTS). The following code provides this:

        private bool JSEval(IWebDriver driver, string script) {
            object result=((IJavaScriptExecutor)driver).ExecuteScript(script);
            if (result==null) {
                return false;
            }else {
                return (bool)result;
            }
        }

        private void JSWait(IWebDriver driver, string script, long timeout)
        {
            int interval = 500;
            long waited = 0;
            while (!JSEval(driver, script) && waited < timeout)
            {
                System.Threading.Thread.Sleep(interval);
                waited += interval;
            }
        }

        private void ClickAndWait(ISelenium selenium,IWebDriver driver, string control)
        {
            selenium.Click(control);
            JSWait(driver, "return Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack();", 1000);
            JSWait(driver, "return !(Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack());", 50000);          
        }

The first two functions duplicate the functionality of a Selenium method called WaitForCondition. The current implementation of WaitForCondition appears to have a bug in it which means it times out after 500ms, but hopefully future some future release will address this so that the calls to JSWait in the above code could be replaced.

The “control” parameter to ClickAndWait is the ID of the control to be clicked. The IDE will create a call to selenium.Click with this ID, so it’s simply a matter of calling ClickAndWait instead where the click prompts an Ajax postback.

May 28, 2011 / pauldundon

Unexpected GETs in Chrome / Safari using ImageButtons

The problem: You have a web page using ImageButtons or other <input type=”image”> controls. This works in IE and Firefox, but generates unexpected GET requests for your page in Chrome and Safari. If you are using ASP.NET webforms, this will appear as a non-postback request.

Possible cause: The src attribute is not set on the input tag at load time (eg, you are setting it in Javascript). In WebKit browsers, this is interpreted as a relative URL so a GET request is made to the host page.

Solution: ensure the src attribute is set in the HTML delivered to the browser, before your script runs.

March 24, 2011 / pauldundon

Even Horizontal Spacing of Menu Items with CSS

The problem: You have a web page with a horizontal menu bar and want to distribute the space between the menu items evenly.

Possible approach: The text-align:justify setting will cause the browser to distribute line space evenly amongst line items on all but the last line of a block element. So, we style our menu items as inline-blocks, and put them in a container with an additional element which assures that the menu items will not form the last line of the overall block.

The following code illustrates the principle:

.hMenu
{
    text-align:justify;
    width:800px;
    margin:0 auto;
}

.filler 
{
    width:100%;
    display: inline-block;
    height:0px;
}

.item 
{
    display: inline-block;
}
<div class="hMenu">
<ul>
<li class="item">&nbsp;</li>
<li class="item">Menu Item 1</li>
<li class="item">Menu Item 2</li>
<li class="item">Menu Item 3</li>
<li class="item">Menu Item 4</li>
<li class="item">&nbsp;</li>
<li class="filler"></li>
</ul>
</div>

The width and margin settings in the hMenu class are not part of the solution but are typical of the way the menu would be styled (and make the spacing of the individual items more obvious). The empty items at either end of the list mean that there will in effect be padding to the left of the first item and to the right of the last item, of the same width of the space between the items.

Credit is due to this article for the original solution.