【发布时间】:2012-06-16 09:42:20
【问题描述】:
我们正在使用 prism 和 WPF 来构建应用程序。最近我们开始使用 UI 自动化 (UIA) 来测试我们的应用程序。但是当我们运行 UIA 测试时发生了一些奇怪的行为。这是简化的外壳:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0" Grid.Column="0"
Name="loadingProgressText"
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="Loading, please wait..."/>
<Border
Grid.Row="0"
x:Name="MainViewArea">
<Grid>
...
</Grid>
</Border>
<!-- Popup -->
<ContentControl
x:Name="PopupContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="PopupRegion"
Focusable="False">
</ContentControl>
<!-- ErrorPopup -->
<ContentControl
x:Name="ErrorContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="ErrorRegion"
Focusable="False">
</ContentControl>
</Grid>
在我们的应用程序中,我们使用层(Popup 和 ErrorPopup)来隐藏 MainViewArea,以拒绝对控件的访问。为了显示Popup,我们使用下一个方法:
//In constructor of current ViewModel we store _popupRegion instance to the local variable:
_popupRegion = _regionManager.Regions["PopupRegion"];
//---
private readonly Stack<UserControl> _popups = new Stack<UserControl>();
public void ShowPopup(UserControl popup)
{
_popups.Push(popup);
_popupRegion.Add(PopupView);
_popupRegion.Activate(PopupView);
}
public UserControl PopupView
{
get
{
if (_popups.Any())
return _popups.Peek();
return null;
}
}
与此类似,我们在应用程序的所有元素上显示ErrorPopup:
// In constructor we store _errorRegion:
_errorRegion = _regionManager.Regions["ErrorRegion"]
// ---
private UserControl _error_popup;
public void ShowError(UserControl popup)
{
if (_error_popup == null)
{
_error_popup = popup;
_errorRegion.Add(_error_popup);
_errorRegion.Activate(_error_popup);
}
}
Mistics...
当我们像用户一样运行它时(双击应用程序图标),我们可以看到两个自定义控件(使用AutomationElement.FindFirst 方法,或通过Visual UI Automation Verify)。但是当我们使用 UI 自动化测试启动它时 - ErrorPopup 从控件树中消失。我们尝试像这样启动应用程序:
System.Diagnostics.Process.Start(pathToExeFile);
我认为我们错过了一些东西。但是什么?
编辑#1
正如@chrismead 所说,我们尝试将UseShellExecute 标志设置为true 来运行我们的应用程序,但这并没有帮助。但是如果我们从 cmd 行启动应用程序,并手动单击按钮,Popup 和 ErrorPopup 在自动化控件树中可见。
Thread appThread = new Thread(delegate()
{
_userAppProcess = new Process();
_userAppProcess.StartInfo.FileName = pathToExeFile;
_userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
_userAppProcess.StartInfo.UseShellExecute = true;
_userAppProcess.Start();
});
appThread.SetApartmentState(ApartmentState.STA);
appThread.Start();
我们的一个建议是,当我们使用方法FindAll 或FindFirst 搜索要单击的按钮时,窗口会以某种方式缓存其 UI 自动化状态,并且不会更新它。
编辑#2
我们发现,prism 库IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) 的扩展方法有一些奇怪的行为。如果我们停止使用它,这将特别解决我们的问题。现在我们可以在PopupContentControl 中看到ErrorView 和任何类型的视图,并且应用程序更新UIA 元素树结构。但这不是答案——“请停止使用此功能”!
在MainViewArea 中,我们有一个ContentControl,它会根据用户操作更新其内容,并且我们只能看到第一个加载到ContentControl.Content 属性的UserControl。这是这样执行的:
IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);
如果我们更改视图,则 UI 自动化树中不会执行任何更新 - 第一个加载的视图将在其中。但视觉上我们观察到另一个View,WPFInspector 正确显示它(它显示的不是 UI 自动化树),但 Inspect.exe - 不是。
我们关于窗口使用某种缓存的建议也是错误的 - 在 UI 自动化客户端中缓存我们必须显式打开,但我们不这样做。
【问题讨论】:
-
那么,说简单的双击启动应用程序会导致控件在树中,但 Process.Start 启动不会正确吗?
-
是的,它是正确的。但是我们尝试了 3 种从代码启动应用程序的方法 - 没有人能让我们找到正确的解决方案...
-
您是否尝试过从 cmd 窗口启动应用程序?如果这样可行,那么使用 ProcessStartInfo.UseShellExecute 标志可能会起作用。
-
我们已经尝试了您的建议。这没有帮助。
-
可能不相关,但在 Silverlight(所以不是 WPF)中,我们的
ContentControl直到实际在屏幕上查看时才会初始化/呈现;具体来说,它的Loaded事件没有被触发。如果浏览器窗口被最小化,或者在其他窗口之后,它永远不会触发Loaded事件。但是,一旦用户恢复窗口或重新组织其内容以便 Silverlight 运行时呈现屏幕,Loaded事件就会自动触发。所以是的,不确定这是否相关,甚至不确定如何解决这个问题以进行自动化测试。
标签: c# wpf xaml devexpress ui-automation