cassini_使用Cassini(ASP.NET Web Matrix / Visual Studio Web Developer)对ASP.NET页,基类,控件和其他小部件进行NUnit单元测试...

cassini

cassini

There's a lot of info out there on how to cobble together NUnit Unit Testing of ASP.NET Pages and assorted goo. NUnitASP is a nice class library to facilitate this kind of testing, but it doesn't solve a few problems:

关于如何将ASP.NET页的NUnit单元测试和各种goo拼凑在一起,有很多信息。 NUnitASP是一个很好的类库,可以促进这种测试,但是它不能解决一些问题:

  • Do you have/want a Web Server on your Test/Build machine?

    您在测试/构建机器上是否有/想要Web服务器?
  • How do you get your Test Pages and such over to the Web Server? Just automatically copy them?

    您如何将测试页等信息传递到Web服务器? 只是自动复制它们?
  • Are your Test cases self-contained? That is, do they require external files and other stuff to be carried along with them?

    您的测试用例是否完整? 也就是说,它们是否需要外部文件和其他东西随身携带?

I have a need to test a number of utility classes, base classes for System.Web.UI.Page and other miscellany and I'd like the tests to be entirely self contained and runnable only with NUnit as a requirement.

我需要测试许多实用程序类,System.Web.UI.Page的基类和其他杂项,我希望测试完全独立并且只能在需要使用NUnit的情况下运行。

So, here's a small solution we use at Corillian. I use Cassini, the tiny ASP.NET Web Server that brokers HTTP Requests to System.Web.Hosting and the ASP.NET Page Renderer. You may know Cassini as the precursor to the Visual Developer Web Server from Visual Studio "Whidbey" 2005. Cassini usually comes with two parts, CassiniWebServer.exe and Cassini.dll.  However, I don't want to launch a executables, so I'll just refer to Cassini.dll as that is the main engine.

因此,这是我们在Corillian使用的一个小解决方案。 我使用Cassini (小型ASP.NET Web服务器),该代理将HTTP请求代理到System.Web.Hosting和ASP.NET Page Renderer。 您可能知道Cassini是Visual Studio“ Whidbey” 2005的Visual Developer Web Server的前身。Cassini通常包括两部分:CassiniWebServer.exe和Cassini.dll。 但是,我不想启动可执行文件,因此我只引用Cassini.dll,因为这是主要引擎。

using Cassini;

使用卡西尼号;

    [TestFixture]

[TestFixture]

    public class WebTests

    公共类WebTest

    {

{

        private Server webServer;

        专用服务器webServer;

        private readonly int webServerPort = 8085;

        private只读int webServerPort = 8085;

        private readonly string webServerVDir = "/";

        私有只读字符串webServerVDir =“ /”;

        private string tempPath = AppDomain.CurrentDomain.BaseDirectory;

        私有字符串tempPath = AppDomain.CurrentDomain.BaseDirectory;

        private string tempBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"bin");

        私有字符串tempBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,“ bin”);

        private string webServerUrl; //built in Setup

        私有字符串webServerUrl; //内置安装程序

Cassini is the 'private Server webServer' in the code above. I'm using a fairly random port, but you could certainly scan for an open port if you like. Note that I'm building a /bin folder, as Cassini's own ASP.NET Hostingn AppDomain will look for DLLs to load in /bin.

Cassini是上面代码中的“专用服务器webServer”。 我使用的是相当随机的端口,但是如果您愿意,您当然可以扫描开放端口。 请注意,我正在建立一个/ bin文件夹,因为Cassini自己的ASP.NET Hostingn AppDomain将查找要在/ bin中加载的DLL。

Cassini starts up another AppDomain, and that AppDomain then loads Cassini.dll AGAIN, except the new AppDomain has a different search path that includes /bin, so it won't find Cassini.dll in the current directory. Usually this problem is solved by putting Cassini.dll in the GAC, but I want this test to be self-contained, and since I'll need my other DLLs in /bin anyway...

Cassini启动另一个AppDomain,然后该AppDomain再次加载Cassini.dll,但新的AppDomain具有包含/ bin的其他搜索路径,因此它将不在当前目录中找到Cassini.dll。 通常,通过将Cassini.dll放入GAC可以解决此问题,但是我希望此测试是独立的,并且因为无论如何我都需要在/ bin中使用其他DLL ...

[TestFixtureSetUp]

[TestFixtureSetUp]

public void Setup()

公共无效Setup()

{

{

    //Extract the web.config and test cases (case sensitive!)

    //提取web.config和测试用例(区分大小写!)

    ExtractResource("web.config",tempPath);

ExtractResource(“ web.config”,tempPath);

    ExtractResource("test1.aspx",tempPath);

ExtractResource(“ test1.aspx”,tempPath);

    ExtractResource("test2.aspx",tempPath);

ExtractResource(“ test2.aspx”,tempPath);

    //NOTE: Cassini is going to load itself AGAIN into another AppDomain,

    //注意:Cassini将再次将自身加载到另一个AppDomain中,

    // and will be getting it's Assembliesfrom the BIN, including another copy of itself!

    //并从BIN中获取它的Assembly,包括它本身的另一个副本!

    // Therefore we need to do this step FIRST because I've removed Cassini from the GAC

    //因此,我们需要首先执行此步骤,因为我已经从GAC中删除了Cassini

    //Copy our assemblies down into the web server's BIN folder

    //将我们的程序集复制到Web服务器的BIN文件夹中

    Directory.CreateDirectory(tempBinPath);

Directory.CreateDirectory(tempBinPath);

    foreach(string file in Directory.GetFiles(tempPath,"*.dll"))

    的foreach(在Directory.GetFiles(TEMPPATH,字符串文件“*。DLL”))

    {

{

        string newFile = Path.Combine(tempBinPath,Path.GetFileName(file));

        字符串newFile = Path.Combine(tempBinPath,Path.GetFileName(file));

        if (File.Exists(newFile)){File.Delete(newFile);}

        如果(File.Exists(newFile)){File.Delete(newFile);}

        File.Copy(file,newFile);

File.Copy(file,newFile);

    }

}

    //Start the internal Web Server

    //启动内部Web服务器

    webServer = new Server(webServerPort,webServerVDir,tempPath);

webServer =新服务器(webServerPort,webServerVDir,tempPath);

    webServerUrl = String.Format("http://localhost:{0}{1}",webServerPort,webServerVDir);

webServerUrl = String.Format(“ http:// localhost:{0} {1}”,webServerPort,webServerVDir);

    webServer.Start();

webServer.Start();

    //Let everyone know

    //让大家知道

    Debug.WriteLine(String.Format("Web Server started on port {0} with VDir {1} in physical directory {2}",webServerPort,webServerVDir,tempPath));

Debug.WriteLine(String.Format(“ Web服务器使用物理目录{2}中的VDir {1}在端口{0}上启动”,webServerPort,webServerVDir,tempPath));

}

}

A couple of important things to note here. 

这里要注意几个重要的事情。

  • This method is marked [TestFixtureSetup] rather than [Setup] as we want it to run only once for this whole Assembly, not once per test.

    此方法标记为[TestFixtureSetup]而不是[Setup],因为我们希望该方法在整个Assembly中只运行一次,而不是每个测试运行一次。
  • We're copying all the DLLs in the current directory down to /bin for Cassini's use during the test, but we'll delete them in [TestFixtureTearDown] after Cassini's AppDomain.Unload.

    我们正在将当前目录中的所有DLL复制到/ bin下,以供Cassini在测试期间使用,但我们将在Cassini的AppDomain.Unload之后在[TestFixtureTearDown]中将其删除。
  • We build webServerUrl and start Cassini (remember, it's the "webServer" variable).

    我们构建webServerUrl并启动Cassini(请记住,它是“ webServer”变量)。
  • At this point we are listing on http://localhost:8085/

    此时,我们在http:// localhost:8085 /上列出

Additionally, I've snuck a new method in, ExtractResource(). This takes the name of an Embedded Resource (one that is INSIDE our test assembly) and puts it into a directory. In this case, I'm using the current AppDomain's directory, but you could certainly use Path.GetTempPath() if you like.

另外,我在ExtractResource()中添加了一个新方法。 这采用嵌入式资源的名称(在我们的测试程序集内部),并将其放入目录中。 在这种情况下,我使用的是当前AppDomain的目录,但是您可以根据需要使用Path.GetTempPath()。

private StringCollection extractedFilesToCleanup = new StringCollection();

私人StringCollection extractedFilesToCleanup = new StringCollection();

private string ExtractResource(string filename, string directory)

私有字符串ExtractResource(字符串文件名,字符串目录)

{

{

    Assembly a = Assembly.GetExecutingAssembly();

程序集a = Assembly.GetExecutingAssembly();

    string filePath = null;

    字符串filePath = null ;

    string path = null;

    字符串路径= null ;

    using(Stream stream = a.GetManifestResourceStream("Corillian.Voyager.Web.Test." + filename))

    使用(流= a.GetManifestResourceStream(“ Corillian.Voyager.Web.Test。” +文件名))

    {

{

        filePath = Path.Combine(directory,filename);

filePath = Path.Combine(目录,文件名);

        path = filePath;

路径= filePath;

        using(StreamWriter outfile = File.CreateText(filePath))

        使用(StreamWriter outfile = File.CreateText(filePath))

        {

{

            using (StreamReader infile = new StreamReader(stream))

            使用(StreamReader infile = new StreamReader(stream))

            {

{

                outfile.Write(infile.ReadToEnd());   

outfile.Write(infile.ReadToEnd());

            }

}

        }

}

    }

}

    extractedFilesToCleanup.Add(filePath);

extractedFilesToCleanup.Add(filePath);

    return filePath;

    返回filePath;

}

}

The ExtractResource method takes a filename and directory (and could, if you like, take a namespace, although I've hardcoded mine) and pulls a file as a Stream of bytes that was embedded as a resource in our Assembly and puts it into a directory. Hence the name, ExtractResource(). There is a StringCollection called extractedFilesToCleanup that keeps track of all the files we'll want to delete at the end of these tests, in [TestFixtureTearDown].

ExtractResource方法采用文件名和目录(并且,如果您愿意,可以采用名称空间,尽管我已经对我进行了硬编码),并将文件作为字节流拉出,并作为资源嵌入到我们的Assembly中,并将其放入目录。 因此,名称为ExtractResource()。 在[TestFixtureTearDown]中,有一个StringCollection称为extractedFilesToCleanup,用于跟踪在这些测试结束时我们要删除的所有文件。

At this point, I've got a web.config (which was important to my tests, and will be looked at by Cassini/System.Web.Hosting) and a test1.aspx and test2.aspx in my current directory. The Cassini Web Server is started up and listing on port 8085 for HttpRequests. I also turned Page Tracing on in the web.config which will allow me to make certain Assertions about what kinds of code were called by the Page and helper classes within the Cassini ASP.NET context.

至此,我有了一个web.config(对我的测试很重要,它将由Cassini / System.Web.Hosting查看)以及当前目录中的test1.aspx和test2.aspx。 Cassini Web服务器已启动,并在端口8085上列出了HttpRequests。 我还在web.config中启用了“页面跟踪”功能,这使我可以对Cassini ASP.NET上下文中的Page和helper类调用哪种代码进行断言。

I'll add a helper method to create HttpRequests and return the response as a string:

我将添加一个辅助方法来创建HttpRequests并将响应作为字符串返回:

private string GetPage(string page)

私有字符串GetPage(字符串页面)

{

{

    WebClient client = new WebClient();

WebClient客户端=新的WebClient();

    string url = new Uri(new Uri(webServerUrl),page).ToString();

    字符串url = new Uri( new Uri(webServerUrl),page).ToString();

    using (StreamReader reader = new StreamReader(client.OpenRead(url)))

    使用(StreamReader reader = new StreamReader(client.OpenRead(url)))

    {

{

        string result = reader.ReadToEnd();

        字符串结果= reader.ReadToEnd();

        return result;

        返回结果;

    }

}

}

}

Now I can write some tests! My tests need to test things like the overridden behavior of custom BasePages (derived from System.Web.UI.Page) as well as some helper functions that require (to be TRULY tested) an HttpContext. However, I don't want the hassle of a CodeBehind or a lot of other files, and certainly not the effort of a whole separate test project, so I'll make my ASPX pages self contained using <% @Assembly %> directives. So, for example: 

现在我可以编写一些测试! 我的测试需要测试诸如自定义BasePage(从System.Web.UI.Page派生)的重写行为,以及一些需要(必须经过测试)HttpContext的辅助函数。 但是,我不需要CodeBehind或其他许多文件的麻烦,当然也不需要整个单独的测试项目的麻烦,因此,我将使用<%@Assembly%>指令使ASPX页面自成一体。 因此,例如:

<%@ Assembly Name="Corillian.Voyager.Web.Common" %><%@ Page Language="C#" Inherits="Corillian.Voyager.Web.Common.SharedBasePage" %><script runat="server">    public void Page_Load(object sender, EventArgs e )    {        Label1.Text = "Hello";    }</script><html>  <body>    <form runat="server">        <p>            <asp:Label id="Label1" runat="server">Label            </asp:Label>        </p>    </form>  </body></html>

<%@程序集名称=“ Corillian.Voyager.Web.Common”%> <%@页面语言=“ C#” Inherits =“ Corillian.Voyager.Web.Common.SharedBasePage” %> <script runat =“ server”> public void Page_Load(object sender,EventArgs e){Label1.Text =“ Hello”; } </ script> <html> <body> <form runat =“ server”> <p> <asp:Label id =“ Label1” runat =“ server”> Label </ asp:Label> </ p> </形式> </ body> </ html>

A test to determine if this page executed successfully might look like:

确定该页面是否成功执行的测试可能类似于:

[Test]

[测试]

public void BasicSmokeTestOfWebServer()

公共无效BasicSmokeTestOfWebServer()

{

{

    string result = GetPage("test1.aspx");

    字符串结果= GetPage(“ test1.aspx”);

    Assert.IsTrue(result.IndexOf("Hello") != -1,"Basic smoke test of test1.aspx didn't find 'Hello' in response!");

Assert.IsTrue(result.IndexOf(“ Hello”)!= -1,“ test1.aspx的基本冒烟测试未找到响应的“ Hello”!);

}

}

Finally, my cleanup code is simple, deleting all the files we extracted as well as toasting the /bin folder.

最后,我的清理代码很简单,删除了我们提取的所有文件,并烘烤了/ bin文件夹。

[TestFixtureTearDown]

[TestFixtureTearDown]

public void TearDown()

公共无效TearDown()

{

{

    try

    尝试

    {

{

        if (webServer != null)

        如果(webServer!= null )

        {

{

            webServer.Stop();

webServer.Stop();

            webServer = null;

webServer = null ;

        }

}

        CleanupResources();

CleanupResources();

        Directory.Delete(tempBinPath,true);

Directory.Delete(tempBinPath, true );

    }

}

    catch{}

    赶上{}

}

}

private void CleanupResources()

私人void CleanupResources()

{

{

    try

    尝试

    {

{

        foreach(string file in extractedFilesToCleanup)

        的foreach(在extractedFilesToCleanup字符串文件)

        {

{

            File.Delete(file);

File.Delete(文件);

        }

}

    }

}

    catch (Exception ex)

    抓住(前例外)

    {

{

        Debug.WriteLine(ex.ToString());

Debug.WriteLine(ex.ToString());

    }

}

}

}

Now the WebServer, Test Cases and Test are nicely self-contained and can be moved to the CruiseControl.NET Continuous Integration Build Server. 

现在,WebServer,测试用例和测试已经完全独立,可以移动到CruiseControl.NET持续集成构建服务器。

Enjoy. I did.

请享用。 是的

翻译自: https://www.hanselman.com/blog/nunit-unit-testing-of-aspnet-pages-base-classes-controls-and-other-widgetry-using-cassini-aspnet-web-matrixvisual-studio-web-developer

cassini

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值