【发布时间】:2010-07-27 16:22:10
【问题描述】:
我目前正在开发一些基于 .Net 的软件 (.Net Framework 3.5 SP1),它通过其 COM 客户端 API(通常称为 TDApiOle80 或 TDApiOle80.TDConnection)与 HP Quality Center 10.0 集成。
我们正在使用 XUnit 1.6.1.1521 和 Gallio 3.1.397.0(从 msbuild 文件调用)
我们经历了一个过程:
- 创建连接
- 运行测试
- 关闭连接
- 处理
- 强制 GC.Collection() / GC.AwaitingPendingFinalizers()
对于每个集成测试 - 每个集成测试都在其 Fact 中配置超时运行。
我们遇到的问题是,它出现在几次测试(比如大约 10 次左右)之后,Quality Center 在调用时会无限期地阻塞 - 并且整个 Gallio 冻结并且不再响应。
最初我们发现 xunit.net 仅将它的超时应用于事实中的代码 - 所以它会无限期地等待构造函数或处置方法完成 - 所以我们将该逻辑移到测试主体中只是为了确认。 ..但这并没有解决问题(运行一定数量的测试后仍然会挂起)。
使用 TestDriven.Net 时也会发生同样的事情 - 可以交互地运行 1 个或几个测试,但超过 10 个测试并且整个运行冻结 - 我们唯一的选择是终止 TD 使用的 ProcessInvocation86.exe 进程。网。
有没有人有关于如何阻止这种情况发生的任何提示/技巧,或者至少将我的集成测试与这些类型的问题隔离开来 - 以便 QC API 无限期阻塞的测试,测试将失败超时并允许 Gallio 进行下一个测试。
更新
使用 STA 线程的提示有助于将问题向前推进一点 - 通过自定义 XUnit.Net 属性,我们现在在它自己的 STA 线程中启动测试。这已经阻止了 Gallio/TestDriven.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;
}
}
相反,我们现在在使用 TestDriven.Net 运行测试时会看到这样的输出 - 顺便说一下,运行同一个套件几次会导致所有测试通过,或者通常只有一两个测试失败。在第一次失败后,第二次失败会导致这个“卸载应用程序域时出错”问题。
测试'IntegrationTests.Execute_Test1' 失败:超出测试执行时间: 30000毫秒
测试 'T:IntegrationTests.Execute_Test2' 失败:卸载时出错 应用程序域。 (HRESULT 的例外情况: 0x80131015) System.CannotUnloadAppDomainException: 卸载 appdomain 时出错。 (来自 HRESULT 的异常:0x80131015) 在 System.AppDomain.Unload(AppDomain 域)在 Xunit.ExecutorWrapper.Dispose() 在 Xunit.Runner.TdNet.TdNetRunner.TestDriven.Framework.ITestRunner.RunMember(ITestListener 听众,大会大会, MemberInfo 成员)在 TestDriven.TestRunner.AdaptorTestRunner.Run(ITestListener 测试监听器,ITraceListener traceListener,字符串组装路径, 字符串 testPath) 在 TestDriven.TestRunner.ThreadTestRunner.Runner.Run()
4 次通过,2 次失败,0 次跳过,接受 50.42 秒 (xunit)。
我仍然无法确定为什么 Quality Center 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);
似乎在 ConnectProjectEx 之后调用 Connect 有可能阻塞(但它是不确定的)。删除冗余连接调用似乎显着提高了测试的稳定性——正确的连接代码:
connection = new TDConnection();
connection.InitConnectionEx(credentials.Host);
connection.ConnectProjectEx(credentials.Domain, credentials.Project, credentials.User, credentials.Password);
继承了代码库,我没有过多考虑连接代码。
我还没有弄清楚为什么即使使用上面包含的超时代码,Thread.Join(timeout) 也永远不会返回。您可以附加一个调试器,它只显示测试线程处于加入/等待操作中。也许与在 STA 线程中执行有关?
【问题讨论】:
-
所以当他们称它为“质量中心”时,他们只是在讽刺?
-
假设我不是该产品的粉丝 - 哦,对于产品中的肥皂或 REST 服务:)
-
我们在 QC 10 的 OTA / TDApiOle80 上执行了数百个集成测试,没有任何问题或阻塞。所以理论上你应该能够做到这一点。您是否尝试过解决阻塞本身的下划线问题。最好理解它为什么会阻塞,而不是试图解决问题。
-
你有没有试过查看被阻塞的线程,它们在哪里阻塞,甚至可能是为什么?
-
关于你的测试过程的问题,为什么每次测试都要关闭连接、处理和运行GC?为什么不让所有测试在同一个连接上运行?
标签: .net com gallio xunit.net hp-quality-center