我目前正在研究一些基于.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文件调用)
我们经历了以下过程:
对于每个集成测试 - 并且每个集成测试都使用在其事实中配置的超时运行。
我们遇到的问题是,经过几次测试后(大约10个左右),质量中心在被叫时无限期阻塞 - 而且整个Gallio冻结并且不再响应。
最初我们发现xunit.net只将其超时应用于事实中的代码 - 所以它会无限期地等待构造函数或dispose方法完成 - 所以我们将这个逻辑移到测试体中以确认。 ..但这并没有解决问题(在运行一定数量的测试后仍然会挂起)。
使用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运行测试时看到这样的输出 - 偶然运行相同的套件几次将导致所有测试通过,或者通常只有1或2个测试失败。在第一次失败后,第二次失败导致“卸载appdomain时出错”问题。
测试'IntegrationTests.Execute_Test1' 失败:超出测试执行时间: 30000ms
测试 'T:IntegrationTests.Execute_Test2' 失败:卸载时出错 应用程序域。 (HRESULT的例外情况: 0x80131015) System.CannotUnloadAppDomainException: 卸载appdomain时出错。 (HRESULT异常:0x80131015) 在System.AppDomain.Unload(AppDomain 域名) Xunit.ExecutorWrapper.Dispose()at Xunit.Runner.TdNet.TdNetRunner.TestDriven.Framework.ITestRunner.RunMember(ITestListener 听众,大会集会, 会员信息成员) TestDriven.TestRunner.AdaptorTestRunner.Run(ITestListener testListener,ITraceListener traceListener,String assemblyPath, 字符串testPath)at 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);
似乎调用Connect后跟ConnectProjectEx有可能阻塞(但它是非确定性的)。删除冗余连接调用似乎显着提高了测试的稳定性 - 正确的连接代码:
connection = new TDConnection();
connection.InitConnectionEx(credentials.Host);
connection.ConnectProjectEx(credentials.Domain, credentials.Project, credentials.User, credentials.Password);
继承了代码库后,我没有多想过连接代码。
我还有一点要弄清楚为什么即使使用上面包含的超时代码,Thread.Join(超时)也永远不会返回。您可以附加调试器,它只显示测试线程处于加入/等待操作中。也许在STA线程中执行会有什么作用?
答案 0 :(得分:1)
您可以尝试在单独的线程上运行代码,然后在超时时调用新线程上的Join
,并在超时时中止它。
例如:
static readonly TimeSpan Timeout = TimeSpan.FromSeconds(10);
public static void RunWithTimeout(ThreadStart method) {
var thread = new Thread(method);
thread.Start();
if (!thread.Join(Timeout)) {
thread.Abort();
Assert.False(true, "Timeout!");
}