<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Dan Crevier's Blog : PageModel</title><link>http://blogs.msdn.com/dancre/archive/tags/PageModel/default.aspx</link><description>Tags: PageModel</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>A pattern for unit testable Asp.net pages: Part 4</title><link>http://blogs.msdn.com/dancre/archive/2007/07/31/a-pattern-for-unit-testable-asp-net-pages-part-4.aspx</link><pubDate>Wed, 01 Aug 2007 06:52:19 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4159856</guid><dc:creator>dancre</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/dancre/comments/4159856.aspx</comments><wfw:commentRss>http://blogs.msdn.com/dancre/commentrss.aspx?PostID=4159856</wfw:commentRss><description>&lt;p&gt;Okay, now for unit tests of the code from &lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/25/a-pattern-for-unit-testable-asp-net-pages-part-3.aspx"&gt;part 3&lt;/a&gt;. The goal is to completely cover RenamePageModel. First we need mock implementations of IPageContext and IDataAccess. For IPageContext, we are going to use special exception types for page not found and server errors. And, we'll add an accessor to track redirects. It's all pretty straightforward.&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MockPageContext : IPageContext
    {
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsSecureConnection
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _isSecureConnection; }
            set { _isSecureConnection = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsPost
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _isPost; }
            set { _isPost = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ThrowPageNotFound()
        {
            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageNotFoundException();
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ThrowServerError()
        {
            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; MockServerErrorException();
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Redirect(&lt;span class="kwrd"&gt;string&lt;/span&gt; destination)
        {
            _redirectString = destination;
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; HasRedirected
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _redirectString != &lt;span class="kwrd"&gt;null&lt;/span&gt;; }
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; RedirectUrl
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _redirectString; }
        }

        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; _isSecureConnection;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; _isPost;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _redirectString;
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MockPageNotFoundException : Exception
    {
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MockServerErrorException : Exception
    {
    }
&lt;/pre&gt;
&lt;p&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
&lt;/p&gt;
&lt;p&gt;The IDataAccess mock is also pretty straightforward. It's simply got properties that can be used to set the return value for the function, or to throw exceptions if desired.&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;class&lt;/span&gt; MockDataAccess : IDataAccess
    {
        &lt;span class="kwrd"&gt;public&lt;/span&gt; MockDataAccess(&lt;span class="kwrd"&gt;string&lt;/span&gt; getItemNameResult, Exception getItemNameException, &lt;span class="kwrd"&gt;string&lt;/span&gt; setItemNameResult, Exception setItemNameException)
        {
            _getItemNameResult = getItemNameResult;
            _getItemNameException = getItemNameException;
            _setItemNameResult = setItemNameResult;
            _setItemNameException = setItemNameException;
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; GetItemName(&lt;span class="kwrd"&gt;string&lt;/span&gt; path)
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (_getItemNameException != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            {
                &lt;span class="kwrd"&gt;throw&lt;/span&gt; _getItemNameException;
            }

            &lt;span class="kwrd"&gt;return&lt;/span&gt; _getItemNameResult;
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; SetItemName(&lt;span class="kwrd"&gt;string&lt;/span&gt; path, &lt;span class="kwrd"&gt;string&lt;/span&gt; newName)
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (_setItemNameException != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            {
                &lt;span class="kwrd"&gt;throw&lt;/span&gt; _setItemNameException;
            }

            &lt;span class="kwrd"&gt;return&lt;/span&gt; _setItemNameResult;
        }

        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _getItemNameResult;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; Exception _getItemNameException;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _setItemNameResult;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; Exception _setItemNameException;
    }
&lt;/pre&gt;
&lt;p&gt;And, now for the actual unit tests. Basically there's a test for each code path on load or post.&lt;/p&gt;&lt;pre class="csharpcode"&gt;    [TestClass]
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; RenamePageModelTests
    {
        [TestMethod]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestLoadSuccess()
        {
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(&lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext());
            Assert.AreEqual(&lt;span class="str"&gt;"name"&lt;/span&gt;, model.Name, &lt;span class="str"&gt;"Name not set from item name"&lt;/span&gt;);
        }

        [TestMethod]
        [ExpectedException(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MockPageNotFoundException))]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestLoadFileNotFoundException()
        {
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; FileNotFoundException(), &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(&lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext());
        }

        [TestMethod]
        [ExpectedException(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MockPageNotFoundException))]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestLoadUnauthorizedAccessException()
        {
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; UnauthorizedAccessException(), &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(&lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext());
        }

        [TestMethod]
        [ExpectedException(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MockServerErrorException))]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestLoadIOException()
        {
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; IOException(), &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(&lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext());
        }

        [TestMethod]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestPostSuccess()
        {
            MockPageContext pageContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext();
            pageContext.IsPost = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="str"&gt;"newpath"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(pageContext);
            Assert.AreEqual(&lt;span class="str"&gt;"rename.aspx?path=newpath"&lt;/span&gt;, pageContext.RedirectUrl, &lt;span class="str"&gt;"Did not redirect to correct url"&lt;/span&gt;);
        }

        [TestMethod]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestPostArgumentException()
        {
            MockPageContext pageContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext();
            pageContext.IsPost = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentException());
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Name = &lt;span class="str"&gt;"errorName"&lt;/span&gt;;
            model.Load(pageContext);
            Assert.AreEqual(&lt;span class="str"&gt;"errorName"&lt;/span&gt;, model.Name, &lt;span class="str"&gt;"New name not returned in model.Name"&lt;/span&gt;);
            Assert.IsNotNull(model.ErrorString, &lt;span class="str"&gt;"Error string not set"&lt;/span&gt;);
        }

        [TestMethod]
        [ExpectedException(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MockPageNotFoundException))]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestPostFileNotFoundException()
        {
            MockPageContext pageContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext();
            pageContext.IsPost = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; FileNotFoundException());
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(pageContext);
        }

        [TestMethod]
        [ExpectedException(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MockPageNotFoundException))]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestPostUnauthorizedAccessException()
        {
            MockPageContext pageContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext();
            pageContext.IsPost = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; UnauthorizedAccessException());
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(pageContext);
        }

        [TestMethod]
        [ExpectedException(&lt;span class="kwrd"&gt;typeof&lt;/span&gt;(MockServerErrorException))]
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; TestPostIOException()
        {
            MockPageContext pageContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockPageContext();
            pageContext.IsPost = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
            MockDataAccess mockDataAccess = &lt;span class="kwrd"&gt;new&lt;/span&gt; MockDataAccess(&lt;span class="str"&gt;"name"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; IOException());
            RenamePageModel model = &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(mockDataAccess, &lt;span class="str"&gt;"foo"&lt;/span&gt;);
            model.Load(pageContext);
        }
&lt;/pre&gt;
&lt;p&gt;If you use Visual Studio .Net's handy code coverage coloring, you'll see that we are covering all the code in the model, except for some closing tags in exceptions that are never hit because of the IPageContext functions that throw exceptions.&lt;/p&gt;
&lt;p&gt;Well, that's it! I've put the entire project on&amp;nbsp;Windows Live&amp;nbsp;SkyDrive:&lt;/p&gt;&lt;iframe style="border-right: #dde5e9 1px solid; padding-right: 0px; border-top: #dde5e9 1px solid; padding-left: 0px; padding-bottom: 0px; margin: 3px; border-left: #dde5e9 1px solid; width: 240px; padding-top: 0px; border-bottom: #dde5e9 1px solid; height: 66px; background-color: #ffffff" marginwidth="0" marginheight="0" src="http://cid-f9dac4c43e9a681d.skydrive.live.com/embedrowdetail.aspx/Blog/PageModelPattern.zip" frameborder="0" scrolling="no"&gt;&lt;/iframe&gt;
&lt;p&gt;Let me know if you have any questions on it. And, let me know if there are other areas you'd like me to write about, such as using the asynchronous model. I'm interested to hear feedback on how this model can be adapted for other types of web apps.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4159856" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/dancre/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blogs.msdn.com/dancre/archive/tags/PageModel/default.aspx">PageModel</category></item><item><title>A pattern for unit testable Asp.net pages: Part 3</title><link>http://blogs.msdn.com/dancre/archive/2007/07/25/a-pattern-for-unit-testable-asp-net-pages-part-3.aspx</link><pubDate>Thu, 26 Jul 2007 07:16:49 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4053965</guid><dc:creator>dancre</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/dancre/comments/4053965.aspx</comments><wfw:commentRss>http://blogs.msdn.com/dancre/commentrss.aspx?PostID=4053965</wfw:commentRss><description> &lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;  &lt;p&gt;In this post, I'm going to give an example of the pattern in action, built on the classes described in the &lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/23/a-pattern-for-unit-testable-asp-net-pages-part-2.aspx"&gt;last post&lt;/a&gt;. For the example, I've chosen something similar to the rename page on &lt;a href="http://folders.live.com"&gt;Live Folders&lt;/a&gt;. Here's how it should work:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The Url should be of the form http://&amp;lt;server&amp;gt;/rename.aspx?path=&amp;lt;path to item&amp;gt;.  &lt;li&gt;If the item does not exist or the user does not have access, a 404 error should be returned.  &lt;li&gt;If there is any error talking to the back end storage, a 500 error should be returned.  &lt;li&gt;The page should have a text box for the name, prepopulated with the current item name.  &lt;li&gt;There should be a submit button.  &lt;li&gt;On submit, it should rename the item.  &lt;ul&gt; &lt;li&gt;If successful, it should redirect to another page (in my example, I just redirect to the rename page with the new path)  &lt;li&gt;If the new name has illegal characters, it should show an error inline.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Let's start by looking at the .aspx. Note: The page actually looks horrible. I made no attempt to make it look pretty!&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Rename.aspx.cs" Inherits="PageModelPattern.RenamePage" %&amp;gt;&lt;/span&gt;
&lt;span class="asp"&gt;&amp;lt;%@ Import Namespace="Microsoft.Security.Application" %&amp;gt;&lt;/span&gt;

&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt; &lt;span class="attr"&gt;html&lt;/span&gt; &lt;span class="attr"&gt;PUBLIC&lt;/span&gt; &lt;span class="kwrd"&gt;"-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;/span&gt; &lt;span class="kwrd"&gt;"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://www.w3.org/1999/xhtml"&lt;/span&gt; &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Rename&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="form1"&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Panel&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt; &lt;span class="attr"&gt;Visible&lt;/span&gt;&lt;span class="kwrd"&gt;="&amp;lt;%# !string.IsNullOrEmpty(this.PageModel.ErrorString) %&amp;gt;"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;="color: Red"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;# &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageModel.ErrorString &lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:Panel&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Name: &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="name"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="&amp;lt;%# AntiXss.HtmlAttributeEncode(this.PageModel.Name) %&amp;gt;"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Button&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;="Submit"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Notice that the page uses the &amp;lt;%# %&amp;gt; binding syntax. For more information about this, see my previous &lt;a href="http://blogs.msdn.com/dancre/archive/2007/02/13/the-difference-between-lt-and-lt-in-asp-net.aspx"&gt;post&lt;/a&gt;. Basically, the expression inside will be evaluated when the page is data bound. This happens after the model has been loaded by PageModelBasedPage. In these expressions, we can access public properties exposed by the mode. We use two properties here. The first it the error string. We have an &amp;lt;asp:Panel&amp;gt; that's visible when the error string is non empty or null. In that case, it will render as a div. There is also an &amp;lt;input&amp;gt; field for the name which is populated from this.PageModel.Name. Since the name will untrusted input, we need to escape it to avoid cross site scripting attacks. I chose to use the &lt;a href="http://msdn2.microsoft.com/en-us/security/aa973814.aspx"&gt;Microsoft AntiXss Library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, let's look at the code behind.&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;partial&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; RenamePage : PageModelBasedPage&amp;lt;RenamePageModel&amp;gt;
    {
        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Create a RenamePageModel.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;RenamePageModel.&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; RenamePageModel CreateModel()
        {
            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; RenamePageModel(&lt;span class="kwrd"&gt;new&lt;/span&gt; DataAccess(), &lt;span class="kwrd"&gt;this&lt;/span&gt;.Request.QueryString[&lt;span class="str"&gt;"path"&lt;/span&gt;]);
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Collect data about a post back.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CollectPostData()
        {
            &lt;span class="kwrd"&gt;base&lt;/span&gt;.CollectPostData();

            &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageModel.Name = &lt;span class="kwrd"&gt;this&lt;/span&gt;.Request.Form[&lt;span class="str"&gt;"name"&lt;/span&gt;];
        }
    }
&lt;/pre&gt;
&lt;p&gt;It's very simple, but that's the point! This is the code we can't unit test. CreateModel creates the RenamePageModel. I'll go into the DataAccess part in a moment. The other thing it passes in is the path, which it grabs from the query string. The other thing it does is populate the name property in the model on a postback from the form value.&lt;/p&gt;
&lt;p&gt;To abstract the access to the data layer, I created a simple interface for the purpose of this demo:&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;interface&lt;/span&gt; IDataAccess
    {
        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Get the name of an item from its path.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;param name="path"&amp;gt;The path to the item.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;The item name.&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="FileNotFoundExcetion"&amp;gt;If the item does not exist.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="UnauthorizedAccessException"&amp;gt;If the user does not have read access to the item.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="IOException"&amp;gt;General i/o exception.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;string&lt;/span&gt; GetItemName(&lt;span class="kwrd"&gt;string&lt;/span&gt; path);

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Sets the name of an item.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;param name="path"&amp;gt;The path to the item.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;param name="newName"&amp;gt;The new name.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;The new path.&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="FileNotFoundExcetion"&amp;gt;If the item does not exist.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="ArgumentException"&amp;gt;If the new name is invalid.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="UnauthorizedAccessException"&amp;gt;If the user does not have write access to the item.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;exception cref="IOException"&amp;gt;General i/o exception.&amp;lt;/exception&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;string&lt;/span&gt; SetItemName(&lt;span class="kwrd"&gt;string&lt;/span&gt; path, &lt;span class="kwrd"&gt;string&lt;/span&gt; newName);
    }
&lt;/pre&gt;
&lt;p&gt;The functions should be pretty self explanatory based on the comments above. The most important part is the exceptions they can throw because the model depends on these. It's abstracted through an interface so that it can be mocked out when unit testing the page model. DataAccess is a placeholder implementation I created for this sample. I won't include the source for it here, but I'll include it in the project that I'll post when I finish the series.&lt;/p&gt;
&lt;p&gt;Now, here's the page model. It has the real business logic that we'll be able to unit test:&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; RenamePageModel : PageModel
    {
        &lt;span class="kwrd"&gt;public&lt;/span&gt; RenamePageModel(IDataAccess dataAccess, &lt;span class="kwrd"&gt;string&lt;/span&gt; path)
        {
            _dataAccess = dataAccess;
            _path = path;
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Gets the name to display in the UI or sets the name to rename to.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Name
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _nameToDisplay; }
            set { _newName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// The error string to display (or null)&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ErrorString
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _errorString; }
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Called when the page loads.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnLoaded()
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.IsPost)
            {
                HandlePost();

            }
            &lt;span class="kwrd"&gt;else&lt;/span&gt;
            {
                &lt;span class="kwrd"&gt;try&lt;/span&gt;
                {
                    &lt;span class="rem"&gt;// Get the name of the item.&lt;/span&gt;
                    _originalName = _dataAccess.GetItemName(_path);
                }
                &lt;span class="kwrd"&gt;catch&lt;/span&gt; (FileNotFoundException)
                {
                    &lt;span class="rem"&gt;// The item does not exist.&lt;/span&gt;
                    &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.ThrowPageNotFound();
                }
                &lt;span class="kwrd"&gt;catch&lt;/span&gt; (UnauthorizedAccessException)
                {
                    &lt;span class="rem"&gt;// The user does not have access.&lt;/span&gt;
                    &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.ThrowPageNotFound();
                }
                &lt;span class="kwrd"&gt;catch&lt;/span&gt; (IOException)
                {
                    &lt;span class="rem"&gt;// Some other error.&lt;/span&gt;
                    &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.ThrowServerError();
                }

                _nameToDisplay = _originalName;
            }
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Handle a post by renaming the item.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; HandlePost()
        {
            &lt;span class="kwrd"&gt;try&lt;/span&gt;
            {
                &lt;span class="kwrd"&gt;string&lt;/span&gt; newPath = _dataAccess.SetItemName(_path, _newName);
                &lt;span class="rem"&gt;// Redirect to next page - just back to the rename page with the new path&lt;/span&gt;
                &lt;span class="rem"&gt;// for the purpose of this example.&lt;/span&gt;
                &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.Redirect(&lt;span class="str"&gt;"rename.aspx?path="&lt;/span&gt; + AntiXss.UrlEncode(newPath));
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt; (FileNotFoundException)
            {
                &lt;span class="rem"&gt;// The item does not exist.&lt;/span&gt;
                &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.ThrowPageNotFound();
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt; (UnauthorizedAccessException)
            {
                &lt;span class="rem"&gt;// The user does not have access.&lt;/span&gt;
                &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.ThrowPageNotFound();
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt; (IOException)
            {
                &lt;span class="rem"&gt;// There was some other error.&lt;/span&gt;
                &lt;span class="kwrd"&gt;this&lt;/span&gt;.PageContext.ThrowServerError();
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt; (ArgumentException)
            {
                &lt;span class="rem"&gt;// The new name has illegal characters.&lt;/span&gt;
                _errorString = &lt;span class="str"&gt;"The name has illegal characters."&lt;/span&gt;;
                _nameToDisplay = _newName;
            }
        }

        &lt;span class="kwrd"&gt;private&lt;/span&gt; IDataAccess _dataAccess;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _path;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _newName;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _originalName;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _nameToDisplay;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _errorString;
    }
&lt;/pre&gt;
&lt;p&gt;Hopefully the code is pretty straightforward to read. It basically follows the requirements I outlined at the beginning of the post.&lt;/p&gt;
&lt;p&gt;The result is that we have straightforward data binding in the .aspx and .aspx.cs which we can't unit test, and a model that only depends on IPageContext and IDataAccess which we can easily mock out for unit testing. The &lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/31/a-pattern-for-unit-testable-asp-net-pages-part-4.aspx"&gt;next post&lt;/a&gt; will wrap things up by showing some unit tests.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4053965" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/dancre/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blogs.msdn.com/dancre/archive/tags/PageModel/default.aspx">PageModel</category></item><item><title>A pattern for unit testable Asp.net pages: Part 2</title><link>http://blogs.msdn.com/dancre/archive/2007/07/23/a-pattern-for-unit-testable-asp-net-pages-part-2.aspx</link><pubDate>Tue, 24 Jul 2007 07:13:56 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4021350</guid><dc:creator>dancre</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/dancre/comments/4021350.aspx</comments><wfw:commentRss>http://blogs.msdn.com/dancre/commentrss.aspx?PostID=4021350</wfw:commentRss><description> &lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;  &lt;p&gt;In &lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/20/a-pattern-for-unit-testable-asp-net-pages-part-1.aspx"&gt;part 1&lt;/a&gt;, I gave an overview of the pattern. In this part, I will describe some of the core classes. Let's start off with the PageModel itself. It's&amp;nbsp;the most simple. It is an abstract class. Subclasses must implement OnLoaded. It has an associated IPageContext (see below) that the model will use to interface with the page.&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; PageModel
    {
        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// The context associated with the model.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; IPageContext PageContext
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _pageContext; }
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Called to load the page model.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;param name="pageContext"&amp;gt;The page context.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Load(IPageContext pageContext)
        {
            _pageContext = pageContext;
            OnLoaded();
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Override to provide behavior for the model.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnLoaded();

        &lt;span class="kwrd"&gt;private&lt;/span&gt; IPageContext _pageContext;
    }&lt;/pre&gt;
&lt;p&gt;The IPageContext will allow the model to get some Asp.net specific information about the page and to do some Asp.net specific operations. When unit testing, a mock implementation of IPageContext can be used to test the model in isolation. IPageContext looks like this:&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;interface&lt;/span&gt; IPageContext
    {
        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Gets if the connection is secure.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsSecureConnection { get; }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Gets if this is a post.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsPost { get; }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Throws a page not found exception.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;void&lt;/span&gt; ThrowPageNotFound();

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Throws a server error.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;void&lt;/span&gt; ThrowServerError();

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Redirects to another page. Note: Execution continues after this call.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;param name="destination"&amp;gt;Page to redirect to.&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;void&lt;/span&gt; Redirect(&lt;span class="kwrd"&gt;string&lt;/span&gt; destination);

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Has a redirect taken place?&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;bool&lt;/span&gt; HasRedirected { get; }
    }
&lt;/pre&gt;
&lt;p&gt;Note: This interface can be extended as needed. The interface here is a simple starting point.&lt;/p&gt;
&lt;p&gt;Now, let's look at PageModelBasedPage, which is a base class for pages that will use PageModels. It's a generic class, parametized by the PageModel subclass. Subclasses of PageModelBasedPage must implement CreateModel to actually create the model. When the page is loaded, it will create the model with an AspPageContext. If it's a post, it will call a function CollectPostData() which can be overridden to collect post data. Finally, when the page load process is finished, it will do data binding on the page, unless there's been a redirect, in which case it will hide the controls on the page to avoid any unnecessary rendering.&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; PageModelBasedPage&amp;lt;ModelType&amp;gt; : Page &lt;span class="kwrd"&gt;where&lt;/span&gt; ModelType : PageModel
    {
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; PageModelBasedPage()
        {
            &lt;span class="kwrd"&gt;this&lt;/span&gt;.LoadComplete += OnLoad;
            &lt;span class="kwrd"&gt;this&lt;/span&gt;.PreRenderComplete += OnPreRenderComplete;
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Derived class must implement CreateModel and return the model.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;returns&amp;gt;PageModel subclass.&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; ModelType CreateModel();

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Base classes can override for any special data binding.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DoDataBinding()
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (_pageContext.HasRedirected)
            {
                &lt;span class="rem"&gt;// No need to render the page on a redirect.&lt;/span&gt;
                &lt;span class="kwrd"&gt;this&lt;/span&gt;.Visible = &lt;span class="kwrd"&gt;false&lt;/span&gt;;
            }
            &lt;span class="kwrd"&gt;else&lt;/span&gt;
            {
                &lt;span class="kwrd"&gt;this&lt;/span&gt;.DataBind();
            }
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Derived class can do any manipulation of the Form values and set properties&lt;/span&gt;
        &lt;span class="rem"&gt;/// on the PageModel after a post.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CollectPostData()
        {
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// The current page model for the page.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;protected&lt;/span&gt; ModelType PageModel
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _model; }
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Called by Asp.net when the page loads.&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnLoad(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)
        {
            _model = CreateModel();
            _pageContext = &lt;span class="kwrd"&gt;new&lt;/span&gt; AspPageContext(&lt;span class="kwrd"&gt;this&lt;/span&gt;);

            &lt;span class="kwrd"&gt;if&lt;/span&gt; (_pageContext.IsPost)
            {
                &lt;span class="rem"&gt;// WARNING: You may be vulerable to cross site request forgery attacks&lt;/span&gt;
                &lt;span class="rem"&gt;// if you do not implement some sort of canary. For more info, see:&lt;/span&gt;
                &lt;span class="rem"&gt;// &lt;a href="http://en.wikipedia.org/wiki/Csrf"&gt;http://en.wikipedia.org/wiki/Csrf&lt;/a&gt;&lt;/span&gt;
                CollectPostData();
            }

            _model.Load(_pageContext);
        }

        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="rem"&gt;/// Called by Asp.net when page loading is complete (including any asynchronous&lt;/span&gt;
        &lt;span class="rem"&gt;/// tasks)&lt;/span&gt;
        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnPreRenderComplete(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)
        {
            DoDataBinding();
        }

        &lt;span class="kwrd"&gt;private&lt;/span&gt; ModelType _model;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; AspPageContext _pageContext;
    }&lt;/pre&gt;
&lt;p&gt;As mentioned in the code, as written, this code is vulnerable to cross site request forgery attacks. The solution to this issue will depend on your application.&lt;/p&gt;
&lt;p&gt;By using OnPreRenderComplete, our pattern is compatible with Asp.net's asynchronous pattern. I can write more about that if there is interest.&lt;/p&gt;
&lt;p&gt;AspPageContext, the IPageContext used by PageModelBasedPage is as follows:&lt;/p&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; AspPageContext : IPageContext
    {
        &lt;span class="kwrd"&gt;public&lt;/span&gt; AspPageContext(Page page)
        {
            _page = page;
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsSecureConnection
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _page.Request.IsSecureConnection; }
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsPost
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _page.IsPostBack; }
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ThrowPageNotFound()
        {
            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpException(404, &lt;span class="str"&gt;"Page not found."&lt;/span&gt;);
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ThrowServerError()
        {
            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; HttpException(500, &lt;span class="str"&gt;"Server error."&lt;/span&gt;);
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Redirect(&lt;span class="kwrd"&gt;string&lt;/span&gt; destination)
        {
            _page.Response.Redirect(destination);
            _hasRedirected = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
        }

        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; HasRedirected
        {
            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _hasRedirected; }
        }

        &lt;span class="kwrd"&gt;private&lt;/span&gt; Page _page;
        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; _hasRedirected;
    }&lt;/pre&gt;
&lt;p&gt;That's it for the basics! To use the pattern, you need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a PageModel subclass with the business logic for the page. 
&lt;li&gt;Derive your Page from PageModelBasedPage. 
&lt;li&gt;Populate any data in the model at creation time or in CollectPostData. 
&lt;li&gt;Use databinding on the page to bind the results to the UI.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;The &lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/25/a-pattern-for-unit-testable-asp-net-pages-part-3.aspx"&gt;next part&lt;/a&gt; will contain a full example.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4021350" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/dancre/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blogs.msdn.com/dancre/archive/tags/PageModel/default.aspx">PageModel</category></item><item><title>A pattern for unit testable Asp.net pages: Part 1</title><link>http://blogs.msdn.com/dancre/archive/2007/07/20/a-pattern-for-unit-testable-asp-net-pages-part-1.aspx</link><pubDate>Sat, 21 Jul 2007 07:35:02 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:3984761</guid><dc:creator>dancre</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/dancre/comments/3984761.aspx</comments><wfw:commentRss>http://blogs.msdn.com/dancre/commentrss.aspx?PostID=3984761</wfw:commentRss><description>&lt;p&gt;I previously &lt;a href="http://blogs.msdn.com/dancre/archive/tags/DM-V-VM/default.aspx"&gt;blogged about&lt;/a&gt;&amp;nbsp;the pattern we used in our&amp;nbsp;WPF application to separate business logic from the presentation,&amp;nbsp;making the&amp;nbsp;business logic&amp;nbsp;view and&amp;nbsp;data models highly unit testable. Now that I'm working on&amp;nbsp;Asp.net code for &lt;a href="http://folders.live.com"&gt;Live Folders&lt;/a&gt;, we've worked to come up with a similar model&amp;nbsp;that separates the business logic from the presentation. The model we use may not apply to everyone, but I thought I'd present it in a series of posts to&amp;nbsp;hopefully help people out and to get some good feedback.&lt;/p&gt; &lt;p&gt;&lt;a href="http://folders.live.com"&gt;Live Folders&lt;/a&gt;&amp;nbsp;is a pretty basic (Web 1.0!) type application that doesn't make heavy use of client-side JavaScript. It also doesn't use view state or any of the complex Asp.net controls. If your application model is significantly different, this pattern may not work for you.&lt;/p&gt; &lt;p&gt;This pattern breaks a page into two parts. The PageModel is the unit testable business logic for the page, which has no Asp.net dependencies. Then, there's the Page subclass, which collects data about the request (query parameters, form post values, etc), runs the model, and then binds data from the model into its controls. The Page subclass is not unit testable because of its Asp.net dependencies (the Asp.net code should tested with scenario tests that automate the browser).&lt;/p&gt; &lt;p&gt;The PageModel may need to do things like redirect the user or throw a page not found error. These sorts of Asp.net dependencies are abstracted out with an interface called IPageContext. A mock implementation is used for unit testing, and an Asp.net specific implementation is used in the real pages.&lt;/p&gt; &lt;p&gt;This series will consist of the following parts:&lt;/p&gt; &lt;p&gt;&lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/23/a-pattern-for-unit-testable-asp-net-pages-part-2.aspx"&gt;Part 2&lt;/a&gt;: The core classes (PageModel, PageModelBasedPage, IPageContext)&lt;/p&gt; &lt;p&gt;&lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/25/a-pattern-for-unit-testable-asp-net-pages-part-3.aspx"&gt;Part 3&lt;/a&gt;: An example&lt;/p&gt; &lt;p&gt;&lt;a href="http://blogs.msdn.com/dancre/archive/2007/07/31/a-pattern-for-unit-testable-asp-net-pages-part-4.aspx"&gt;Part 4&lt;/a&gt;: Unit testing the example&lt;/p&gt; &lt;p&gt;The resulting code is available at &lt;a href="http://folders.live.com/self.aspx/+drExD6aaB0/Blog/PageModelPattern.zip"&gt;http://folders.live.com/self.aspx/+drExD6aaB0/Blog/PageModelPattern.zip&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3984761" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/dancre/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blogs.msdn.com/dancre/archive/tags/PageModel/default.aspx">PageModel</category></item></channel></rss>