我目前工作的一些基于.NET的软件时,隔离不好的COM组件(HP质量中心10.0)(.NET Framework 3.5 SP1中)与惠普质量中心10.0集成通过它的COM客户端API(通常被称为TDApiOle80或TDApiOle80.TDConnection)。如何从.NET应用程序进行集成测试
我们正在使用的xUnit 1.6.1.1521和加利奥3.1.397.0(从一个MSBuild文件调用)
我们经历的过程:
- 创建连接
- 运行测试
- 结算连接
- 处置
- 强制GC.Collection()/ GC.AwaitingPendingFinalizers( )
对于每个集成测试 - 并且每个集成测试都在其事实中配置的超时运行。
我们的问题是,它出现了一些测试之后(比如大约10个左右),质量中心块称为无限时 - 和整个加利奥的冻结,将不再回应。
最初我们发现xunit.net只应用了事实中的代码超时 - 所以它会无限期地等待构造函数或者处理方法来完成 - 所以我们将这个逻辑移到了测试体中,以确认......但这并没有解决这个问题(仍然挂在天边一定数量的测试之后)。
使用TestDriven.Net时会发生同样的情况 - 可以交互式运行1个或几个测试,但更多的是大约10个测试,整个运行冻结 - 我们唯一的选择是杀死TD使用的ProcessInvocation86.exe进程。净。
有没有人有任何提示/技巧,要么如何阻止这一切发生在一起,要么至少将我的集成测试与这些类型的问题隔离开来 - 以便QC API无限期地阻止测试,测试将失败超时并允许Gallio移动到下一个测试。
更新
倾向于使用STA线程的提示,帮助移动问题上向前迈进了一下 - 通过自定义属性XUnit.Net我们现在在它自己的STA线程启动测试。这已停止加利奥/ TestDriven.Net从完全锁定了,这样我们就可以包括运行我们的哈德森构建服务器上的集成测试。
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或两个失败的测试。和第一次失败之后,在这第二次失败的结果“错误而卸载应用程序域”的问题。
测试 'IntegrationTests.Execute_Test1' 失败:测试执行时间超过: 30000ms
测试 “T:IntegrationTests。Execute_Test2' 失败:卸载时出错 appdomain。 (异常来自HRESULT: 0x80131015) System.CannotUnloadAppDomainException: 卸载appdomain时出错。在System.AppDomain.Unload(应用程序域 域)处 Xunit.Runner.TdNet.TdNetRunner.TestDriven.Framework.ITestRunner.RunMember Xunit.ExecutorWrapper.Dispose()(ITestListener 听者 : (0x80131015从HRESULT异常) ,装配组件, 的MemberInfo构件)在 TestDriven.TestRunner.AdaptorTestRunner.Run(ITestListener testListener,ITraceListener 的TraceListener,字符串assemblyPath, 字符串testPath)在 TestDriven.TestRunner.ThreadTestRunner.Runner.Run()
4通过,2失败,0跳过,拿到了 50.42秒(xunit)。
我还没有确定为什么Quality Center API随机无限期悬挂 - 很快就会进行调查。
更新27/07/2010
我终于确立了挂的原因 - 这里是有问题的代码:
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线程中执行一些操作?
所以当他们把它称为“质量中心”,他们只是被讽刺? – Gabe 2010-07-19 02:44:26
我们只是说我不是产品的粉丝 - 哦,对于产品中的肥皂或REST服务:) – Bittercoder 2010-07-19 04:09:34
我们在OTA/TDApiOle80上为QC 10执行了数百次集成测试,没有问题或阻塞。所以理论上你应该可以做同样的事情。 您是否尝试解决阻塞本身的下划线问题。 也许最好理解它为什么会阻塞,而不是试图解决问题。 – 2010-07-19 20:21:48