我目前正在开发一些基于.NET的软件(.NET Framework 3.5 SP1),它通过COM客户端API(通常称为TDapiole80或TDapiole80.TDconnection)与HP Quality Center 10.0集成。
我们正在使用xunit 1.6.1.1521和galio 3.1.397.0(从msbuild文件调用)
我们经历了一个过程:
-
创建连接
-
运行测试
-
闭合连接
-
处置
-
强制gc.collection()/gc.waitingPendingFinalizers()。
对于每个集成测试——并且每个集成测试都是在配置超时的情况下运行的。
我们遇到的问题是,在几次测试(比如大约10次左右)后,质量中心会无限期地被调用,而整个galio都会冻结,不再响应。
最初,我们发现xunit.net只将它的超时应用于事实中的代码-因此它将无限期地等待构造函数或释放方法完成-所以我们将该逻辑移入测试体中,只是为了确认…但这并没有解决问题(在运行一定数量的测试之后仍然会挂起)。
同样的事情也发生在使用testsdriven.net时-可以交互运行1个或几个测试,但超过10个测试,整个运行将冻结-我们唯一的选择是终止td.net使用的processinvocation86.exe进程。
是否有人对如何一起阻止这一切发生,或者至少将我的集成测试与这些问题隔离开来有任何提示/诀窍-以便在qc api无限期阻塞的测试中,测试将在超时时失败,并允许galio进入下一个测试。
更新
使用sta线程的提示有助于将问题向前推进一点——通过一个定制的xunit.net属性,我们现在在它自己的sta线程中启动测试。这阻止了galio/testsdriven.net完全锁定,因此我们可以在我们的Hudson构建服务器上运行集成测试。
public class StaThreadFactAttribute : FactAttribute
{
const int DefaultTime = 30000; // 30 seconds
public StaThreadFactAttribute()
{
Timeout = DefaultTime;
}
protected override System.Collections.Generic.IEnumerable<Xunit.Sdk.ITestCommand> EnumerateTestCommands(Xunit.Sdk.IMethodInfo method)
{
int timeout = Timeout;
Timeout = 0;
var commands = base.EnumerateTestCommands(method).ToList();
Timeout = timeout;
return commands.Select(command => new StaThreadTimeoutCommand(command, Timeout, method)).Cast<ITestCommand>();
}
}
public class StaThreadTimeoutCommand : DelegatingTestCommand
{
readonly int _timeout;
readonly IMethodInfo _testMethod;
public StaThreadTimeoutCommand(ITestCommand innerComand, int timeout, IMethodInfo testMethod)
: base(innerComand)
{
_timeout = timeout;
_testMethod = testMethod;
}
public override MethodResult Execute(object testClass)
{
MethodResult result = null;
ThreadStart work = delegate
{
try
{
result = InnerCommand.Execute(testClass);
var disposable = testClass as IDisposable;
if (disposable != null) disposable.Dispose();
}
catch (Exception ex)
{
result = new FailedResult(_testMethod, ex, this.DisplayName);
}
};
var thread = new Thread(work);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
if (!thread.Join(_timeout))
{
return new FailedResult(_testMethod, new Xunit.Sdk.TimeoutException((long)_timeout), base.DisplayName);
}
return result;
}
}
相反,我们现在在用testsdriven.net运行测试时会看到这样的输出——偶然地运行同一个套件几次将导致所有测试通过,或者通常只有一到两个测试失败。在第一次失败之后,第二次失败会导致“卸载AppDomain时出错”问题。
测试“integrationtests.execute”测试1
失败:超过测试执行时间:
300万毫秒
试验
't:integrationtests.execute_test2'
失败:卸载时出错
AppDealm。(来自hresult的异常:
0x80131015)
System.CanNotUnloadAppDomain异常:
卸载AppDomain时出错。
(来自hresult的异常:0x80131015)
在System.AppDomain.Unload(AppDomain
域)
xUnit.ExecutorWrapper.Dispose()位于
xUnit.runner.tdnet.tdnet runner.testdriven.framework.itestrunner.runmember(itestListener)
侦听器,程序集,
memberinfo成员)位于
TestDriven.TestRunner.adaptortStrunner.run(itestListener)
测试侦听器,itraceListener
traceListener,字符串assemblyPath,
字符串testpath)位于
TestDriven.TestRunner.ThreadTestRunner.Runner.Run()。
4通过,2失败,0跳过,采取
50.42秒(Xunit)。
我还没有确定为什么质量中心API会无限期地随机挂起-将在不久后对此进行进一步调查。
更新日期:2010年7月27日
我最终确定了挂起的原因-下面是有问题的代码:
connection = new TDConnection();
connection.InitConnectionEx(credentials.Host);
connection.Login(credentials.User, credentials.Password);
connection.Connect(credentials.Domain, credentials.Project);
connection.ConnectProjectEx(credentials.Domain, credentials.Project, credentials.User, credentials.Password);
调用connect之后再调用connectprojectex似乎有可能被阻塞(但这是不确定性的)。删除冗余连接调用似乎大大提高了测试的稳定性-正确的连接代码:
connection = new TDConnection();
connection.InitConnectionEx(credentials.Host);
connection.ConnectProjectEx(credentials.Domain, credentials.Project, credentials.User, credentials.Password);
继承了代码库之后,我没有考虑过连接代码。
有一件事我还没有弄清楚,为什么即使上面包含了超时代码,thread.join(timeout)也永远不会返回。您可以附加一个调试器,它只显示测试线程正在进行连接/等待操作。可能与在STA线程中执行有关?