.net Cassini/WebServer.WebDev、NUnit和AppDomainUnloadexception
我正在使用Cassini/WebServer.WebDev来运行一些使用NUnit的Web服务的自动化测试 我没有做任何花哨的事,只是.net Cassini/WebServer.WebDev、NUnit和AppDomainUnloadexception,.net,nunit,cassini,.net,Nunit,Cassini,我正在使用Cassini/WebServer.WebDev来运行一些使用NUnit的Web服务的自动化测试 我没有做任何花哨的事,只是 public class WebService{ Microsoft.VisualStudio.WebHost.Server _server; public void Start(){ _server = new Microsoft.VisualStudio.WebHost.Server(_port, "/", _physicalPath);
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 '<No Name>' (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).
[…]
线程0x1974已退出,代码为0(0x0)。
############################################################################
##############S U C E S#################
############################################################################
已执行测试:5
忽略的测试:0
失败的测试:0
未处理的异常:4
总时间:257092944秒
############################################################################
线程0x1bd4已退出,代码为0(0x0)。
线程0x10f8已退出,代码为0(0x0)。
线程“”(0x1a80)已退出,代码为0(0x0)。
System.Web.dll中发生了类型为“System.AppDomainUnloadexception”的首次意外异常
#####运行时发生未处理的异常
System.AppDomainUnloadexException:尝试访问卸载的AppDomain。
在System.Web.Hosting.ApplicationManager.HostingEnvironmentShutdownComplete(字符串appId,IAApplicationHost appHost)
在System.Web.Hosting.HostingEnvironment.OnAppDomainUnload(Object unusedObject,EventArgs unusedEventArgs)
mscorlib.dll中首次出现类型为“System.Threading.ThreadAbortException”的异常
mscorlib.dll中首次出现类型为“System.Threading.ThreadAbortException”的异常
System.Web.dll中出现类型为“System.Threading.ThreadAbortException”的第一次意外异常
线程0x111c已退出,代码为0(0x0)。
程序“[0x1A64]nunit-console.exe:Managed”已退出,代码为-100(0xFFFF9C)。
有人知道是什么原因导致了这一切吗?我也有同样的问题,但没有使用卡西尼号。相反,我有自己的基于
System.Net.HttpListener
的web服务器托管,通过System.web.HttpRuntime
提供ASP.Net支持,在通过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
),因为您需要在Cassini创建的应用程序域中调用System.Web.HttpRuntime.Close()
来托管ASP.Net
以下是我使用嵌入式web主机进行的工作单元测试的一些片段,以供参考
MyWebServerHost
类是一个非常小的类,它允许将请求封送到由System.Web.Hosting.ApplicationHost.CreateApplicationHost()创建的应用程序域中
WebServerContext
类只是System.Net.HttpListenerContext
实例的包装,该实例派生自System.MarshallByRefObject,以允许来自新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
类的一个实现,该类通过WebServerContext
类从ASP.Net托管域回调到我的域中
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托管域将使用myWebServerHost
类作为代理创建,以允许交互。还将启动一个System.Net.HttpListener
实例,并启动一个单独的线程来接受连接。建立连接时,线程池中会启动一个工作线程,再次通过myWebServerHost
类来处理请求。最后,当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));
}
}
最后,这个初始化
类用NUnit[SetupFixture]属性标记,用于在单元测试启动时启动web服务器,并在单元测试完成时关闭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();
}
}
我知道这并不能完全回答这个问题,但我希望您能发现这些信息很有用。您有没有找到解决这个问题的方法?我有一个类似的问题。不是一个/good/解决方案,但我最终在NUnit任务上设置了failonerror=“false”,并使用一个单独的工具对xml输出进行后处理,该工具忽略了未处理的AppDomainUnloadexception。我已将一个示例解决方案上载到适当的NUnit bug。如果您可以尝试一下并交叉检查它的有效性(并将您的发现发布到launchpad)以供参考,这将是非常棒的,这也会在评论和任何最终解决方案中报告,其中包含完整的源代码示例?我想知道,特定的测试会是什么样子?它的主体是否会被包装在某种执行调用中,以委托actua
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();
}
}