我正在使用Cassini/WebServer.WebDev使用NUnit运行WebService的一些自动测试.
我没有做任何花哨的事,只是
public class WebService{ Microsoft.VisualStudio.WebHost.Server _server; public void Start(){ _server = new Microsoft.VisualStudio.WebHost.Server(_port, "/", _physicalPath); } public void Dispose() { if (_server != null) { _server.Stop(); _server = null; } } } [TestFixture] public void TestFixture{ [Test] public void Test(){ using(WebService webService = new WebService()){ webService.Start(); // actual test invoking the webservice } } }
,但是当我使用nunit-console.exe运行它时,我得到以下输出:
NUnit version 2.5.0.9015 (Beta-2) Copyright (C) 2002-2008 Charlie Poole.\r\nCopyright (C) 2002-2004 James W. Newki rk, Michael C. Two, Alexei A. Vorontsov.\r\nCopyright (C) 2000-2002 Philip Craig .\r\nAll Rights Reserved. Runtime Environment - OS Version: Microsoft Windows NT 6.0.6001 Service Pack 1 CLR Version: 2.0.50727.1434 ( Net 2.0.50727.1434 ) ProcessModel: Default DomainUsage: Default Execution Runtime: net-2.0.50727.1434 ..... Tests run: 5, Errors: 0, Failures: 0, Inconclusive: 0 Time: 28,4538451 seconds Not run: 0, Invalid: 0, Ignored: 0, Skipped: 0 Unhandled exceptions: 1) TestCase1 : System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. 2) TestCase2 : System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. 3) TestCase3 : System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. 4) TestCase4 : System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain.
如果我在调试器下运行nunit-console,我会在调试控制台中获得以下输出:
[...] The thread 0x1974 has exited with code 0 (0x0). ############################################################################ ############## S U C C E S S ################# ############################################################################ Executed tests : 5 Ignored tests : 0 Failed tests : 0 Unhandled exceptions : 4 Total time : 25,7092944 seconds ############################################################################ The thread 0x1bd4 has exited with code 0 (0x0). The thread 0x10f8 has exited with code 0 (0x0). The thread '' (0x1a80) has exited with code 0 (0x0). A first chance exception of type 'System.AppDomainUnloadedException' occurred in System.Web.dll ##### Unhandled Exception while running System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain. at System.Web.Hosting.ApplicationManager.HostingEnvironmentShutdownComplete(String appId, IApplicationHost appHost) at System.Web.Hosting.HostingEnvironment.OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll A first chance exception of type 'System.Threading.ThreadAbortException' occurred in System.Web.dll The thread 0x111c has exited with code 0 (0x0). The program '[0x1A64] nunit-console.exe: Managed' has exited with code -100 (0xffffff9c).
有没有人有任何想法可能导致这个?
我有同样的问题,但没有使用卡西尼.相反,我有自己的Web服务器托管基于System.Net.HttpListener
ASP.Net支持通过System.Web.HttpRuntime
运行在通过创建的不同的应用程序域System.Web.Hosting.ApplicationHost.CreateApplicationHost()
.这基本上是Cassini的工作方式,除了Cassini在套接字层工作并实现了System.Net.HttpListener
自己提供的许多功能.
无论如何,为了解决我的问题,我需要System.Web.HttpRuntime.Close()
在让NUnit卸载我的应用程序域之前调用.我Close()
通过在我的[SetupFixture]类的[TearDown]方法调用并且该方法调用的宿主代理类中公开一个新方法来完成此操作System.Web.HttpRuntime.Close()
.
我通过.Net Reflector查看了Cassini实现,虽然它使用了System.Web.HttpRuntime.ProcessRequest()
,但它似乎并没有调用System.Web.HttpRuntime.Close()
任何地方.
我不确定如何继续使用预先构建的Cassini实现(Microsoft.VisualStudio.WebHost.Server
),因为您需要System.Web.HttpRuntime.Close()
在Cassini创建的应用程序域内进行调用以托管ASP.Net.
作为参考,这里有一些我的工作单元测试与嵌入式Web托管.
我的WebServerHost
类是一个非常小的类,它允许将编组请求放入由创建的应用程序域中System.Web.Hosting.ApplicationHost.CreateApplicationHost()
.
using System; using System.IO; using System.Web; using System.Web.Hosting; public class WebServerHost : MarshalByRefObject { public void Close() { HttpRuntime.Close(); } public void ProcessRequest(WebServerContext context) { HttpRuntime.ProcessRequest(new WebServerRequest(context)); } }
本WebServerContext
类只是围绕着一个包装System.Net.HttpListenerContext
,从System.MarshalByRefObject派生,以允许新的ASP.Net宿主域调用回调到我的域实例.
using System; using System.Net; public class WebServerContext : MarshalByRefObject { public WebServerContext(HttpListenerContext context) { this.context = context; } // public methods and properties that forward to HttpListenerContext omitted private HttpListenerContext context; }
本WebServerRequest
类只是抽象的实现System.Web.HttpWorkerRequest
是回调到经由该ASP.Net托管域我域类WebServerContext
类.
using System; using System.IO; using System.Web; class WebServerRequest : HttpWorkerRequest { public WebServerRequest(WebServerContext context) { this.context = context; } // implementation of HttpWorkerRequest methods omitted; they all just call // methods and properties on context private WebServerContext context; }
该WebServer
班是用于启动和停止web服务器的控制器.启动时,ASP.Net托管域是使用我的WebServerHost
类作为代理来创建的,以允许交互.System.Net.HttpListener
还启动了一个实例,并启动了一个单独的线程来接受连接.建立连接后,在线程池中启动工作线程以再次通过我的WebServerHost
类来处理请求.最后,当Web服务器停止时,监听器停止,控制器等待线程接受连接退出,然后关闭监听器.最后,HTTP运行时也通过调用WebServerHost.Close()
方法关闭.
using System; using System.IO; using System.Net; using System.Reflection; using System.Threading; using System.Web.Hosting; class WebServer { public static void Start() { lock ( typeof(WebServer) ) { // do not start more than once if ( listener != null ) return; // create web server host in new AppDomain host = (WebServerHost)ApplicationHost.CreateApplicationHost ( typeof(WebServerHost), "/", Path.GetTempPath() ); // start up the HTTP listener listener = new HttpListener(); listener.Prefixes.Add("http://*:8182/"); listener.Start(); acceptConnectionsThread = new Thread(acceptConnections); acceptConnectionsThread.Start(); } } public static void Stop() { lock ( typeof(WebServer) ) { if ( listener == null ) return; // stop listening; will cause HttpListenerException in thread blocked on GetContext() listener.Stop(); // wait connection acceptance thread to exit acceptConnectionsThread.Join(); acceptConnectionsThread = null; // close listener listener.Close(); listener = null; // close host host.Close(); host = null; } } private static WebServerHost host = null; private static HttpListener listener = null; private static Thread acceptConnectionsThread; private static void acceptConnections(object state) { while ( listener.IsListening ) { try { HttpListenerContext context = listener.GetContext(); ThreadPool.QueueUserWorkItem(handleConnection, context); } catch ( HttpListenerException e ) { // this exception is ignored; it will be thrown when web server is stopped and at that time // listening will be set to false which will end the loop and the thread } } } private static void handleConnection(object state) { host.ProcessRequest(new WebServerContext((HttpListenerContext)state)); } }
最后,这个Initialization
标有NUnit [SetupFixture]属性的类用于在单元测试启动时启动Web服务器,并在完成单元测试时将其关闭.
using System; using NUnit.Framework; [SetUpFixture] public class Initialization { [SetUp] public void Setup() { // start the local web server WebServer.Start(); } [TearDown] public void TearDown() { // stop the local web server WebServer.Stop(); } }
我知道这并没有完全回答这个问题,但我希望你能找到有用的信息.