Hello, I am experiencing a difficulty with running ASPX pages outside of IIS and even outside of HttpRuntime. These practices are not well documented, but I hope someone has already solved similar problem.
My goal is simple: Somehow create an instance of an ASPX page, run it and test what happens. Test values of some page's properties, properties of its controls and so on.
Here's where I came:
- <div mce_keep="true">Create a new application domain (ApplicationHost.CreateApplicationHost).</div>
- <div mce_keep="true">Compile the page and get its type (ClientBuildManager).</div>
- <div mce_keep="true">Create a new HttpContext.</div>
- <div mce_keep="true">Construct the page (Type.GetConstructor...).</div>
- <div mce_keep="true">Then I can subscribe some events, if I want to test the page during its life-cycle.</div>
- <div mce_keep="true">Run the page (page.ProcessRequest). Documentation says that this method is "not intended to be used directly from your code". However I didn't find any other way...</div>
And it works. This is the full code of a sample test fixture:
using System; using System.Web; using System.Web.Compilation; using System.Web.Hosting; using System.Web.UI; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; namespace PageSharp.Incubator.Tests.Learning { /// <summary> /// Test how could pages be programatically instantiated and executed. /// </summary> [TestFixture] public class PageLifeCycleLearningTest : MarshalByRefObject { private string _virtualDir = "/"; private string _appDir; private PageLifeCycleLearningTest _host; public PageLifeCycleLearningTest() { _appDir = System.IO.Directory.GetCurrentDirectory().TrimEnd("bin".ToCharArray()); } [TestFixtureSetUp] public void SetUp() { _host = (PageLifeCycleLearningTest) ApplicationHost.CreateApplicationHost(typeof(PageLifeCycleLearningTest), _virtualDir, _appDir); } [Test] public void TestSomeFact() { _host.TestSomeFact_Run(); } private void TestSomeFact_Run() { var page = CreatePage("~/default.aspx", String.Empty); page.PreInit += new EventHandler(TestSomeFact_PreInit); page.ProcessRequest(HttpContext.Current); var ctl = (System.Web.UI.WebControls.Label) page.FindControl("ctl00$ContentPlaceHolder1$WebUserControl11$Label1"); Assert.That(ctl.Text, Is.EqualTo("Hello World!")); } private void TestSomeFact_PreInit(object sender, EventArgs e) { var page = (Page)sender; var ctl = (System.Web.UI.WebControls.Label)page.FindControl("ctl00$ContentPlaceHolder1$WebUserControl11$Label1"); Assert.That(ctl, Is.Null); } /// <summary> /// Compile and instantiate a page. /// </summary> /// <param name="virtualPath">Virtual path.</param> /// <param name="queryString">Query string.</param> /// <returns>Instance of the page.</returns> private Page CreatePage(string virtualPath, string queryString) { var cbm = new ClientBuildManager(_virtualDir, _appDir); var type = cbm.GetCompiledType(virtualPath); var writer = new System.IO.StringWriter(); HttpContext.Current = new HttpContext(new SimpleWorkerRequest(virtualPath, queryString, writer)); Page page = (Page)type.GetConstructor(new Type[] { }).Invoke(new object[] { }); return page; } } }
There's just one remaining problem: A few moments after the test runs and passes, something throws System.AppDomainUnloadedException exception, with no stack trace available:
I can't find a way to avoid it. Probably I have to clean the AppDomain somehow, but how?