NUnit与ASP.NET网站

我正在尝试升级我们的构建服务器,从没有构建服务器到拥有一个!

我正在使用JetBrains的TeamCity (使用ReSharper几年,我相信他们的东西),并打算使用NUnit和MSBuild 。

但是,我提出了一个问题:似乎无法使用NUnit测试ASP.NET网站。 我曾经假设可以将它配置为在构建之后测试App_Code,但似乎很好地进行测试的唯一方法是将网站转换为Web应用程序(我的老板不喜欢这个想法)。

我怎么能这样做?

请记住,测试需要能够从TeamCity自动触发。

  • 如果您想抽吸测试您的网站,或在某些端点上爆炸 – 请参阅下面的代码。

  • 另一方面,如果你想测试不可测试的(可伪造的)ASP.NET网站程序集(而不是Web应用程序),那么就像他们在法国所说的那样,SOL

程序集是一个随机命名的动态编译程序集,深入框架临时ASP.NET文件的大小,使得测试几乎不可能。

你真的需要考虑几个选项:

  1. 将需要测试的逻辑放在单独的程序集中。
  2. 更改为提供可测试程序集的Web应用程序项目。

对不起,我不认为你会找到你想要的东西,但我可能是错的。 让我们来看看。

祝好运


下载带有站点和应用程序的Visual Studio 2008示例 。

我为WebHost.WebServer.dll编写了一个包装器,它是开发服务器的核心,在CI中运行良好。 我用它所有的时间。

这是一个按比例缩小的版本,包括一个用法示例。

test.cs中

 using System.Net; using NUnit.Framework; namespace Salient.Excerpts { [TestFixture] public class WebHostServerFixture : WebHostServer { [TestFixtureSetUp] public void TestFixtureSetUp() { // debug/bin/testproject/solution/siteundertest - make sense? StartServer(@"..\..\..\..\TestSite"); // is the equivalent of // StartServer(@"..\..\..\..\TestSite", // GetAvailablePort(8000, 10000, IPAddress.Loopback, true), "/", "localhost"); } [TestFixtureTearDown] public void TestFixtureTearDown() { StopServer(); } [Test] public void Test() { // while a reference to the web app under test is not necessary, // if you do add a reference to this test project you may F5 debug your tests. // if you debug this test you will break in Default.aspx.cs string html = new WebClient().DownloadString(NormalizeUri("Default.aspx")); } } } 

WebHostServer.cs

 // Project: Salient // http://salient.codeplex.com // Date: April 16 2010 using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Threading; using Microsoft.VisualStudio.WebHost; namespace Salient.Excerpts { ///  /// A general purpose Microsoft.VisualStudio.WebHost.Server test fixture. /// WebHost.Server is the core of the Visual Studio Development Server (WebDev.WebServer). /// /// This server is run in-process and may be used in F5 debugging. ///  ///  /// If you are adding this source code to a new project, You will need to /// manually add a reference to WebDev.WebHost.dll to your project. It cannot /// be added from within Visual Studio. /// /// Please see the Readme.txt accompanying this code for details. ///  /// NOTE: code from various namespaces/classes in the Salient project have been merged into this /// single class for this post in the interest of brevity public class WebHostServer { private Server _server; public string ApplicationPath { get; private set; } public string HostName { get; private set; } public int Port { get; private set; } public string VirtualPath { get; private set; } public string RootUrl { get { return string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}{2}", HostName, Port, VirtualPath); } } ///  /// Combine the RootUrl of the running web application with the relative url specified. ///  public virtual Uri NormalizeUri(string relativeUrl) { return new Uri(RootUrl + relativeUrl); } ///  /// Will start "localhost" on first available port in the range 8000-10000 with vpath "/" ///  ///  public void StartServer(string applicationPath) { StartServer(applicationPath, GetAvailablePort(8000, 10000, IPAddress.Loopback, true), "/", "localhost"); } ///  ///  /// Physical path to application. /// Port to listen on. /// Optional. defaults to "/" /// Optional. Is used to construct RootUrl. Defaults to "localhost" public void StartServer(string applicationPath, int port, string virtualPath, string hostName) { if (_server != null) { throw new InvalidOperationException("Server already started"); } // WebHost.Server will not run on any other IP IPAddress ipAddress = IPAddress.Loopback; if(!IsPortAvailable(ipAddress, port)) { throw new Exception(string.Format("Port {0} is in use.", port)); } applicationPath = Path.GetFullPath(applicationPath); virtualPath = String.Format("/{0}/", (virtualPath ?? string.Empty).Trim('/')).Replace("//", "/"); _server = new Server(port, virtualPath, applicationPath, false, false); _server.Start(); ApplicationPath = applicationPath; Port = port; VirtualPath = virtualPath; HostName = string.IsNullOrEmpty(hostName) ? "localhost" : hostName; } ///  /// Stops the server. ///  public void StopServer() { if (_server != null) { _server.Stop(); _server = null; // allow some time to release the port Thread.Sleep(100); } } public void Dispose() { StopServer(); } ///  /// Gently polls specified IP:Port to determine if it is available. ///  ///  ///  public static bool IsPortAvailable(IPAddress ipAddress, int port) { bool portAvailable = false; for (int i = 0; i < 5; i++) { portAvailable = GetAvailablePort(port, port, ipAddress, true) == port; if (portAvailable) { break; } // be a little patient and wait for the port if necessary, // the previous occupant may have just vacated Thread.Sleep(100); } return portAvailable; } ///  /// Returns first available port on the specified IP address. /// The port scan excludes ports that are open on ANY loopback adapter. /// /// If the address upon which a port is requested is an 'ANY' address all /// ports that are open on ANY IP are excluded. ///  ///  ///  /// The IP address upon which to search for available port. /// If true includes ports in TIME_WAIT state in results. /// TIME_WAIT state is typically cool down period for recently released ports. ///  public static int GetAvailablePort(int rangeStart, int rangeEnd, IPAddress ip, bool includeIdlePorts) { IPGlobalProperties ipProps = IPGlobalProperties.GetIPGlobalProperties(); // if the ip we want a port on is an 'any' or loopback port we need to exclude all ports that are active on any IP Func isIpAnyOrLoopBack = i => IPAddress.Any.Equals(i) || IPAddress.IPv6Any.Equals(i) || IPAddress.Loopback.Equals(i) || IPAddress.IPv6Loopback. Equals(i); // get all active ports on specified IP. List excludedPorts = new List(); // if a port is open on an 'any' or 'loopback' interface then include it in the excludedPorts excludedPorts.AddRange(from n in ipProps.GetActiveTcpConnections() where n.LocalEndPoint.Port >= rangeStart && n.LocalEndPoint.Port <= rangeEnd && ( isIpAnyOrLoopBack(ip) || n.LocalEndPoint.Address.Equals(ip) || isIpAnyOrLoopBack(n.LocalEndPoint.Address)) && (!includeIdlePorts || n.State != TcpState.TimeWait) select (ushort)n.LocalEndPoint.Port); excludedPorts.AddRange(from n in ipProps.GetActiveTcpListeners() where n.Port >= rangeStart && n.Port <= rangeEnd && ( isIpAnyOrLoopBack(ip) || n.Address.Equals(ip) || isIpAnyOrLoopBack(n.Address)) select (ushort)n.Port); excludedPorts.AddRange(from n in ipProps.GetActiveUdpListeners() where n.Port >= rangeStart && n.Port <= rangeEnd && ( isIpAnyOrLoopBack(ip) || n.Address.Equals(ip) || isIpAnyOrLoopBack(n.Address)) select (ushort)n.Port); excludedPorts.Sort(); for (int port = rangeStart; port <= rangeEnd; port++) { if (!excludedPorts.Contains((ushort)port)) { return port; } } return 0; } } } 

注意: Microsoft.VisualStudio.WebHost命名空间包含在WebDev.WebHost.dll文件中。 此文件位于GAC中,但无法在Visual Studio中添加对此程序集的引用。

要添加引用,您需要在文本编辑器中打开.csproj文件并手动添加引用。

查找包含项目引用的ItemGroup并添加以下元素:

  False  

参考: http : //www.codeproject.com/KB/aspnet/test-with-vs-devserver-2.aspx