Большой пласт работы с тестами UI составляют Desktop приложения под Windows. В разных статьях во время написания карт пользовательского интерфейса (PageObject’s) читателю показывают написание локаторов вручную с помощью UISpy.
Пять лет назад количество таких элементов в моих тестах стало слишком велико, контролы стали сложнее, а библиотеки для UI тестирования начали расти как грибы.
Рутину по поиску необходимого XPath было решено преобразовать в радость.
Вначале использовал Visual Studio UIMap Editor: чем сложнее были приложения, тем меньше он меня не устраивал в плане скорости и подстановки локаторов. Причины опишу в отдельной статье по UIAutomation и его обертках (TestStack.White, Cuite, Microsoft UITest и т.д.).
Итак, задача
Сделать простой редактор PageObject’s. Вспомогательные инструменты: UIAVerify.
Общие принципы
- каждый контрол (Control) рабочего стола обладает поисковыми свойствами и может содержать дочерние элементы
- контролы группируются в элементы (Element), элементы группируются в пространства имен (Assemblies)
- Инкапсуляция, наследование, полиморфизм
- Возможность использовать уже готовые карты. Например, у меня есть уже готовые карты и расширения для DevExpress, AvalonDock, OpenFileDialog и т.д. Я их переношу между проектами
Что получилось
На примере сложения в калькуляторе Windows 10:
- Составляю карту для окна «Калькулятор», для этого в редакторе карт из дерева рабочего стола переношу все объекты окна, и убираю лишнее. Можно добавлять элементы и вручную (например, для Selenium), полет фантазии безграничен
- Смотрю на сгенерированные названия элементов. Если они мне не нравятся, переименовываю вручную
- Сохраняю карту для обертки UIAutomation (Обычный конвертер XML => C#. Обертка может использоваться любая, Selenium, MS UITest, TestStack.White)
Время на все: минуты три
Справа – аналог дерева рабочего стола UIAVerify. Слева: карта
Результат
Под версионный контроль отправляется 4 файла:
<?xml version="1.0" encoding="utf-8"?>
<Map Version="2.0.0.0" Uid="7b797972-8c3c-4dbf-9c30-7cc8ae14afda">
<Assembly Name="Calc" FrameWork="NavyWindows">
<Assembly Name="Map" FrameWork="NavyWindows">
<Element ControlType="Window" Name="Калькулятор" Comment="Window "Калькулятор"">
<Control Comment="Window "TitleBar"" IsHidden="true" Name="Калькулятор" ControlType="Window">
<Property Name="Name">Калькулятор</Property>
<Control Comment="Button "Close"" Name="ЗакрытьКалькулятор" ControlType="Button">
<Property Name="AutomationId">Close</Property>
</Control>
...
</Map>
namespace Calc
{
public partial class UIКалькуляторWindow : UIWindow
{
public UIКалькуляторWindow(UIControl control) { Wrap(control); }
public UIКалькуляторWindow() { }
public static UIКалькуляторWindow Instance
{
get { return Desktop.Instance.Get<UIКалькуляторWindow>(XPath); }
}
public static By XPath { get { return By.Xpath("Name=Калькулятор"); } }
/// <summary>
/// Button "plusButton"
/// </summary>
public UIButton UIПлюсButton
{
get { return Get<UIButton>(By.Xpath(@"AutomationId=plusButton")); }
}
...
</Map>
namespace Calc
{
public static class UIКалькуляторWindowExt
{
public static int Result(this UIКалькуляторWindow window)
{
var textValue = window.UIResultText.Text
.Replace("На экране показано", "")
.Replace(" ", "");
return int.Parse(textValue);
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Calc;
using System.Diagnostics;
using System;
namespace CalcTests
{
[TestClass]
public class CalculationTests : IDisposable
{
public CalculationTests()
{
_process = Process.Start("calc.exe");
_calc = UIКалькуляторWindow.Instance;
}
[TestMethod]
public void Sum()
{
_calc.UIОдинButton.Click();
_calc.UIПлюсButton.Click();
_calc.UIПятьButton.Click();
_calc.UIРавноButton.Click();
Assert.AreEqual(6, _calc.Result(), "Не совпадает результат");
}
public UIКалькуляторWindow _calc { get; set; }
Process _process;
public void Dispose()
{
_calc.Close();
}
}
}
Думаю, альтернативных решений уже много, ссылки и более удачные примеры кидайте в комментариях.
Спасибо за внимание!
Автор: RussianTM