使用Xamarin UITest在iOS选择器中选择一项?

时间:2019-04-03 08:07:13

标签: ios picker xamarin.uitest

我们正在使用UITest测试Xamarin.Forms应用,并需要在iOS上选择Picker的值。

可以按如下所示在项目集合中选择行号:

app.Query(x => x.Class("UIPickerView").Invoke("selectRow", 1, "inComponent", 0, "animated", true))

在上面的示例中,我们选择第1行。

但是,我们需要能够按值选择-例如,对于具有标题列表的Picker,要选择“ Mrs”标题,我们需要执行以下操作:

app.Query(x => x.Class(“ UIPickerView”)。Invoke(“ selectValue”,“ Mrs”,“ inComponent”,0,“动画”,true))

但是,UIPickerView(引用here)上没有可用的selectValue(或类似方法)。

一种想法是获取项目中的行数,并遍历它们-但是当我尝试调用getNumberOfRows时,出现以下错误:

  

app.Query(x => x.Class(“ UIPickerView”)。Invoke(“ numberOfRows”,   “ inComponent”,0))

     

执行Query时出错([未知])异常:System.Exception:   调用iOS选择器需要0或不均匀的数量   参数(它们必须成对匹配,包括方法名)。在   Xamarin.UITest.Queries.InvokeHelper.AppTypedSelector   (Xamarin.UITest.Queries.AppQuery appQuery,   Xamarin.UITest.Queries.ITokenContainer tokenContainer,System.Object []   queryParams,System.String methodName,System.Object []参数,   System.Boolean显式请求值)[0x0011a]在   <2a16c16730a54859bda72c6bc1c728f7>:0在   Xamarin.UITest.Queries.InvokeHelper.Invoke   (Xamarin.UITest.Queries.AppQuery appQuery,System.String methodName,   System.Object []参数)中的[0x00000]   <2a16c16730a54859bda72c6bc1c728f7>:0在   Xamarin.UITest.Queries.AppQuery.Invoke(System.String methodName,   System.Object arg1,System.Object arg2)[0x00000]在   <2a16c16730a54859bda72c6bc1c728f7>:0在   .m__0   (Xamarin.UITest.Queries.AppQuery x)[0x0000b]在   <0de9804cff324d049415e25573e8da8a>:0在   Xamarin.UITest.SharedApp.Expand [T](System.Func 2[T,TResult] typedQuery) [0x0000c] in <2a16c16730a54859bda72c6bc1c728f7>:0 at Xamarin.UITest.iOS.iOSApp+<>c__DisplayClass17_0 1 [T] .b__0()   在<2a16c16730a54859bda72c6bc1c728f7>:0中的[0x00000]   Xamarin.UITest.Utils.ErrorReporting.With [T](System.Func`1 [TResult]   func,System.Object [] args,System.String memberName)[0x0000e]在   <2a16c16730a54859bda72c6bc1c728f7>:0异常:错误   执行查询([未知])

错误原因很清楚-该方法期望一对特定的参数,但是我看不到如何正确地编写查询。

那么,关于如何通过值而不是行号选择和/或如何获得项目数的任何想法/指针?

2 个答案:

答案 0 :(得分:0)

好的...我有一些有效的方法。

前提是我们在基础项目列表中选择行,然后检查Picker是否正在显示所需的值。

注释 1.我有一个UIElement帮助器类,它使我可以获取基础Control或AutomationId。

  1. 使用了以下两种方法,以便我可以直接从TestPages调用SelectPickerValue或使用TapAndSelectPickerValue-我应该整理一下。

  2. 如果当前值在可用值的中间,我就无法以其他方式滚动。

  3. 如果该项目在视图中,则可以只点击它-但如果不是,则我们必须进行滚动/行选择。

       internal static void TapAndSelectPickerValue(UIElement element, string v, int maxRows = 10)
        {
            app.Tap(element.UIControl);
            SelectPickerValue(v, maxRows, element.AutomationId);
        }

        internal static void SelectPickerValue(string v, int maxItems = 10,string pickerId = "")
        {
            if (_platform == Platform.iOS)
            {
                bool managedToSelect = false;

                // Check that we haven't already got the value we want selected.
                Xamarin.UITest.Queries.AppResult[] valueCheckResult = app.Query(x => x.Text(v).Id(pickerId));

                if (valueCheckResult.Length == 0)
                {
                    int rowNumber = 0;

                    // Select rows until we either reach the max to try, or we get the one we want selected
                    for (rowNumber = 0; rowNumber < maxItems; rowNumber++)
                    {
                        app.Query(x => x.Class("UIPickerView").Invoke("selectRow", rowNumber, "inComponent", 0, "animated", true));

                        // This is a hack to move the item so that Xamarin Forms will generate the event to update the UI
                        var rect = app.Query(x => x.Class("UIPickerTableView").Index(0).Child()).First().Rect;
                        app.DragCoordinates(rect.CenterX, rect.CenterY, rect.CenterX, rect.CenterY + 22);
                        app.DragCoordinates(rect.CenterX, rect.CenterY, rect.CenterX, rect.CenterY - 22);

                        // check to see if the Picker is now displaying the value we want
                        valueCheckResult = app.Query(x => x.Text(v).Id(pickerId));
                        if (valueCheckResult.Length == 1)
                        {
                            managedToSelect = true;
                            break;
                        }

                    }
                }

                // If we couldn't select the value we wanted, raise an exception
                if (!managedToSelect)
                {
                    throw new Exception($"Could not select value '{v}' for picker '{pickerId}'");
                }

                // Close the Picker
                app.Tap(c => c.Text("Done"));
            }
            else
            {
                app.ScrollDownTo(m => m.Text(v), x => x.Id("select_dialog_listview"));
                app.Tap(x => x.Text(v));
            }
        }

答案 1 :(得分:0)

一个更好的解决方案是编写一个UITest后门,然后在后门中找到Picker的Renderer并直接对其进行控制。

@LandLu在Xamarin论坛上向我提供了解决方案的核心:

您应该首先获取当前的视图控制器。然后迭代子视图以获取选择器渲染器。 这是我的依赖服务,供您参考:

[assembly: Dependency(typeof(FindViewClass))]
namespace Demo.iOS
{
    public class FindViewClass : IFindView
    {
        public void FindViewOfClass()
        {
            UIViewController currentViewController = topViewController();
            getView(currentViewController.View);
        }

        List<PickerRenderer> pickerList = new List<PickerRenderer>();
        void getView(UIView view)
        {
            if (view is PickerRenderer)
            {
                pickerList.Add((PickerRenderer)view);
            }
            else
            {
                foreach (UIView subView in view.Subviews)
                {
                    getView(subView);
                }               
            }
        }

        UIViewController topViewController()
        {
            return topViewControllerWithRootViewController(UIApplication.SharedApplication.KeyWindow.RootViewController);
        }

        UIViewController topViewControllerWithRootViewController(UIViewController rootViewController)
        {
            if (rootViewController is UITabBarController)
            {
                UITabBarController tabbarController = (UITabBarController)rootViewController;
                return topViewControllerWithRootViewController(tabbarController.SelectedViewController);
            }
            else if (rootViewController is UINavigationController)
            {
                UINavigationController navigationController = (UINavigationController)rootViewController;
                return topViewControllerWithRootViewController(navigationController.VisibleViewController);
            }
            else if (rootViewController.PresentedViewController != null)
            {
                UIViewController presentedViewController = rootViewController.PresentedViewController;
                return topViewControllerWithRootViewController(presentedViewController);
            }
            return rootViewController;
        }
    }
}

完成此操作后,可以通过“渲染器”的Element属性选择所需的索引/项目。

修改 我已经将其分解为GitHub repository,它实现了一组后门程序,使选择iOS Picker更加容易