当应用程序通过UI自动化测试开始,但其可见当应用程序通过用户开始ContentControl中是不可

2019-07-29 22:06发布

我们使用棱镜和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>

在我们的应用程序,我们使用图层( PopupErrorPopup )隐藏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);
        }
    }

神秘主义者...

当我们运行它,因为用户这样做(在应用程序图标双击),我们可以看到两个自定义控件(使用AutomationElement.FindFirst方法,或通过视觉UI自动化验证 )。 但是,当我们使用UI自动化测试启动- ErrorPopup在前看不见从控件的树。 我们试图启动这样的应用程序:

System.Diagnostics.Process.Start(pathToExeFile);

我认为,我们错过了什么。 不过什么?

编辑#1

作为@chrismead说,我们想跑我们的应用程序UseShellExecute标志设置为true,但这并不能帮助。 但是,如果我们开始从CMD行应用程序,并手动点击按钮, PopupErrorPopup是自动化控制树可见。

    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();

我们的一个建议是,当我们使用方法FindAllFindFirst搜索按钮,点击,窗口莫名其妙缓存的UI自动化状态,并且不更新它。

编辑#2我们已经发现,棱镜库是扩展方法IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView))有一些奇怪的行为。 如果我们停止使用它,这解决我们的问题格外。 现在我们能看到ErrorView和任何形式的视图PopupContentControl ,和应用程序更新UIA元素树结构。 但是,这不是一个答案 - “只要停止使用该功能”!

MainViewArea我们有一个ContentControl ,这将更新其内容根据用户的行为,我们都能够看到只有第一个加载的UserControlContentControl.Content财产。 这是这样进行的:

IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);

如果我们改变了看法,没有更新将在UI Automation树进行 - 第一加载视图将在它来代替。 但是,在视觉上我们观察到另一个View ,并WPFInspector正确显示它(它的节目不是UI Automation树),但Inspect.exe -不是。

同时我们建议,即窗口使用某种缓存是错误的 - 缓存在UI自动化客户端,我们必须明确地打开,但我们不这样做。

Answer 1:

我很抱歉,我已经错过了一些细节,这是关键的答案。 我认为,这不是重要的事情。 无论如何。

我们使用NavBarDevExpress的控件库WPF。 当什么证明,是NavBar是存在的,动态创建的视图没有对UI Automation树出现。 当从窗口中删除它,有看到所有动态加载的观点的能力。 什么是NavBar -仍然MISTIC我。

这里明亮的例子,看看发生了什么,如果NavBar是窗口存在或不存在(的DevExpress是必需的)。

MainWindow.xaml:

<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
        x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        >
    <Grid Name="ContentGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <!--Comment NavBar to see dynamic control in UI Automation tree-->
        <dxn:NavBarControl Name="asdasd">
            <dxn:NavBarControl.Groups>
                <dxn:NavBarGroup Header="asdasdasdasd" />
            </dxn:NavBarControl.Groups>
        </dxn:NavBarControl>
        <TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
        <Button Grid.Row="1" Content="Create controls" Height="25"  Click="Button_Click"/>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        TextBox tb = new TextBox();
        Grid.SetRow(tb, 1);
        Grid.SetColumn(tb, 1);
        tb.Text = "dynamic is not visible, if NavBar here...";
        ContentGrid.Children.Add(tb);
    }
}

编辑

按照DevExpress的答案在他们的支持网站:

创建同行之后,自动化的事件监听可能会导致性能问题。 我们已经决定清除自动化事件的调用列表来解决这个问题。 在您的具体情况,你需要禁用清除。 要做到这一点,请设置静态DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled属性设为False在窗口构造函数。

这种解决问题。



Answer 2:

我的猜测是, ContentControl的自动化同级应更新其子女AutomationPeer.ResetChildrenCache()的看法已经改变了。

AutomationPeer.InvalidatePeer()应具有相同的效果(除了其它副作用),并且它应该是响应于所述自动调用LayoutUpdated事件。 您可能要检查LayoutUpdated事件引发的看法发生变化时。



Answer 3:

stukselbax,试图找到击键序列(标签和一个进入最有可能的),点击,使您可以看到项目的按钮。 这是很容易地发送按键,我可以添加更多的在这里关于是否适合你。 你总是可以建立你的应用程序,使得用户最有意义的Tab键顺序。

------在12年6月20日更新--------

你试过双击快捷方式到你的应用程序中使用的PInvoke,看看你可以看到控件,当它被打开这种方式在桌面上? 下面是这里的例子在计算器的链接:

导演鼠标事件函数[DllImport(“user32.dll中”)单击,双击

另一个想法:一些我目前自动化,直到他们发生了鼠标点击不树出现在应用程序的控制。 要做到这一点,而无需使用任何硬编码的坐标,我发现在树这仅仅是(高于/低于/等),我需要点击才能到现在控制的地方被发现。 然后我得到了该项目的鼠标坐标,并把鼠标在小胶印机从那里,点击。 然后,我可以找到我的树控件。 如果应用程序的大小时,搬来搬去,等等。这仍会起作用,因为小偏移仍然有效。



文章来源: ContentControl is not visible when application starts via UI Automation test, but its visible when application starts by user