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:
-
Create a new application domain (ApplicationHost.CreateApplicationHost).
-
Compile the page and get its type (ClientBuildManager).
-
Create a new HttpContext.
-
Construct the page (Type.GetConstructor...).
-
Then I can subscribe some events, if I want to test the page during its life-cycle.
-
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...
And it works. This is the full code of a sample test fixture:
1 using System;
2 using System.Web;
3 using System.Web.Compilation;
4 using System.Web.Hosting;
5 using System.Web.UI;
6 using NUnit.Framework;
7 using NUnit.Framework.SyntaxHelpers;
8
9 namespace PageSharp.Incubator.Tests.Learning
10 {
11 /// <summary>
12 /// Test how could pages be programatically instantiated and executed.
13 /// </summary>
14 [TestFixture]
15 public class PageLifeCycleLearningTest : MarshalByRefObject
16 {
17 private string _virtualDir = "/";
18 private string _appDir;
19 private PageLifeCycleLearningTest _host;
20
21 public PageLifeCycleLearningTest()
22 {
23 _appDir = System.IO.Directory.GetCurrentDirectory().TrimEnd("bin".ToCharArray());
24 }
25
26 [TestFixtureSetUp]
27 public void SetUp()
28 {
29 _host = (PageLifeCycleLearningTest)
30 ApplicationHost.CreateApplicationHost(typeof(PageLifeCycleLearningTest), _virtualDir, _appDir);
31 }
32
33 [Test]
34 public void TestSomeFact()
35 {
36 _host.TestSomeFact_Run();
37 }
38
39 private void TestSomeFact_Run()
40 {
41 var page = CreatePage("~/default.aspx", String.Empty);
42 page.PreInit += new EventHandler(TestSomeFact_PreInit);
43 page.ProcessRequest(HttpContext.Current);
44
45 var ctl = (System.Web.UI.WebControls.Label) page.FindControl("ctl00$ContentPlaceHolder1$WebUserControl11$Label1");
46 Assert.That(ctl.Text, Is.EqualTo("Hello World!"));
47 }
48
49 private void TestSomeFact_PreInit(object sender, EventArgs e)
50 {
51 var page = (Page)sender;
52 var ctl = (System.Web.UI.WebControls.Label)page.FindControl("ctl00$ContentPlaceHolder1$WebUserControl11$Label1");
53 Assert.That(ctl, Is.Null);
54 }
55
56 /// <summary>
57 /// Compile and instantiate a page.
58 /// </summary>
59 /// <param name="virtualPath">Virtual path.</param>
60 /// <param name="queryString">Query string.</param>
61 /// <returns>Instance of the page.</returns>
62 private Page CreatePage(string virtualPath, string queryString)
63 {
64 var cbm = new ClientBuildManager(_virtualDir, _appDir);
65 var type = cbm.GetCompiledType(virtualPath);
66 var writer = new System.IO.StringWriter();
67 HttpContext.Current = new HttpContext(new SimpleWorkerRequest(virtualPath, queryString, writer));
68 Page page = (Page)type.GetConstructor(new Type[] { }).Invoke(new object[] { });
69
70 return page;
71 }
72 }
73 }
74
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?