代码之家  ›  专栏  ›  技术社区  ›  Brad Bruce

如何测试客户端程序对web服务器错误的反应?

  •  1
  • Brad Bruce  · 技术社区  · 14 年前

    我有一个基于LibCurl的客户端程序。我遇到过这样的情况:在某些情况下,服务器上的程序运行的时间比配置的IIS超时时间长。然后,IIS终止程序并向客户端返回502错误状态。

    我已经向客户端添加了捕获此问题的代码。现在,我需要找到一种方法来证明这种改变是有效的。

    我无法可靠地再现这个问题,所以没有一个好的测试用例。

    有什么建议吗?

    2 回复  |  直到 14 年前
        1
  •  0
  •   Sky Sanders    14 年前

    这以一种非常简单的方式向您展示了如何在NUnit fixture中嵌入工作进程。

    下面是一个示例装置:

    using System.Net;
    using NUnit.Framework;
    
    namespace Salient.Excerpts
    {
        [TestFixture]
        public class WebHostServerFixture : WebHostServer
        {
            [TestFixtureSetUp]
            public void TestFixtureSetUp()
            {
                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"));
            }
        }
    }
    

    这是服务器:

    // 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
    {
        /// <summary>
        /// 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.
        /// </summary>
        /// <remarks>
        /// 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.
        /// </remarks>
        /// 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); }
            }
    
            /// <summary>
            /// Combine the RootUrl of the running web application with the relative url specified.
            /// </summary>
            public virtual Uri NormalizeUri(string relativeUrl)
            {
                return new Uri(RootUrl + relativeUrl);
            }
    
            /// <summary>
            /// Will start "localhost" on first available port in the range 8000-10000 with vpath "/"
            /// </summary>
            /// <param name="applicationPath"></param>
            public void StartServer(string applicationPath)
            {
                StartServer(applicationPath, GetAvailablePort(8000, 10000, IPAddress.Loopback, true), "/", "localhost");
            }
    
            /// <summary>
            /// </summary>
            /// <param name="applicationPath">Physical path to application.</param>
            /// <param name="port">Port to listen on.</param>
            /// <param name="virtualPath">Optional. defaults to "/"</param>
            /// <param name="hostName">Optional. Is used to construct RootUrl. Defaults to "localhost"</param>
            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;
            }
    
            /// <summary>
            /// Stops the server.
            /// </summary>
            public void StopServer()
            {
                if (_server != null)
                {
                    _server.Stop();
                    _server = null;
                    // allow some time to release the port
                    Thread.Sleep(100);
                }
            }
    
            public void Dispose()
            {
                StopServer();
            }
    
    
           /// <summary>
            /// Gently polls specified IP:Port to determine if it is available.
            /// </summary>
            /// <param name="ipAddress"></param>
            /// <param name="port"></param>
            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;
            }
    
            /// <summary>
            /// 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.
            /// </summary>
            /// <param name="rangeStart"></param>
            /// <param name="rangeEnd"></param>
            /// <param name="ip">The IP address upon which to search for available port.</param>
            /// <param name="includeIdlePorts">If true includes ports in TIME_WAIT state in results.
            /// TIME_WAIT state is typically cool down period for recently released ports.</param>
            /// <returns></returns>
            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<IPAddress, bool> 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<ushort> excludedPorts = new List<ushort>();
    
                // 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文件并手动添加引用。

    <Reference Include="WebDev.WebHost, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86">
      <Private>False</Private>
    </Reference> 
    

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

        2
  •  0
  •   Dave W. Smith    14 年前

    你可能不需要一个完整的IIS,只需要一个足够像IIS的东西来提供正确的响应。选择一种语言,您会发现一个库(可能还有演示),用于组装一个简单的web服务器。