Писане на автоматизирани тестове за тестване на потребителския интерфейс на десктоп приложения

Формулиране на проблема

Например, ще имаме проста задача - има приложение с два бутона. При натискане на първия в текстовото поле ще се появи определен текст (нека бъде „Хабрахабр“). Като щракнете върху втория, там също ще се покажат текущият час и дата.

Съответно са необходими поне три теста за следните случаи:

  1. Първоначалният текст в текстовото поле при стартиране на приложението.
  2. Текстът след щракване върху първия бутон.
  3. Текстът след щракване върху втория бутон.

Преглед на съществуващите решения

1. .Net UI автоматизация

AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "заглавие на прозореца на нашето приложение" )); * Този изходен код беше маркиран с инструмента за открояване на изходния код.

За специфични контролиUI Automationпредоставя набор от допълнителни обвивки, наречениAutomationPatterns, катоExpandCollapsePattern,SelectionItemPatternи т.н., позволявайки съответно да се използва функционалността, специфична за тези контроли, например възможността за разширяване/свиване на разширителя.

Предимства
недостатъци
Примери за тестове към задачата

  1. [Метод на тестване]
  2. public void TestStartup()
  3. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly().Location), @"..\..\..\TestUI\bin\Debug\TestUI.exe" );
  4. var process = Process.Start(appPath);
  5. опитвам
  6. Thread.Sleep(5000);
  7. var mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
  8. var buttonControl = mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));
  9. var textBoxControl = mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
  10. var textBox = (ValuePattern)textBoxControl.GetCurrentPattern(ValuePattern.Pattern);
  11. Assert.AreEqual( "123123123" , textBox.Current.Value);
  12. >
  13. накрая
  14. process.Kill();
  15. >
  16. >
* Този изходен код беше подчертан с инструмента за открояване на изходния код.

  1. [Метод на тестване]
  2. public void TestMethodUIAutomation()
  3. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly().Location), @"..\..\..\TestUI\bin\Debug\TestUI.exe" );
  4. var process = Process.Start(appPath);
  5. опитвам
  6. Thread.Sleep(5000);
  7. var mainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
  8. var buttonControl = mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));
  9. var textBoxControl = mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
  10. var textBox = (ValuePattern) textBoxControl.GetCurrentPattern(ValuePattern.Pattern);
  11. Assert.AreEqual( "123123123" , textBox.Current.Value);
  12. var button = (InvokePattern) buttonControl.GetCurrentPattern(InvokePattern.Pattern);
  13. button.Invoke();
  14. Assert.AreEqual( "Habrahabr" , textBox.Current.Value);
  15. >
  16. накрая
  17. process.Kill();
  18. >
  19. >
* Този изходен код беше маркиран с инструмента за открояване на изходния код.

Текстът след нажатия на втората кнопка да се реализира не се получи, виждайки това, че текущото време винаги е различно, а замокира какво-или не можем.

2. Бял проект

Безплатна кодова рамка, базирана наUI Automation. Предимствата и недостатъците са еднакви, единствената разлика е по-удобен и разширен API за работа с контролното дърво.

Примери за тестове към задачата

  1. [Метод на тестване]
  2. public void TestStartup()
  3. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly().Location), @"..\..\..\TestUI\bin\Debug\TestUI.exe" );
  4. var application = White.Core.Application.Launch(appPath);
  5. Assert.IsNotNull(приложение);
  6. varwindow = application.GetWindow( "MainWindow");
  7. var textBox = window.Get();
  8. Assert.IsNotNull(текстово поле);
  9. Assert.AreEqual( "123123123" , textBox.Text);
  10. >
* Този изходен код беше подчертан с инструмента за открояване на изходния код.

  1. [Метод на тестване]
  2. public void TestWithWhite()
  3. var appPath = Path.Combine(Path.GetDirectoryName( Assembly .GetExecutingAssembly().Location), @"..\..\..\TestUI\bin\Debug\TestUI.exe" );
  4. var application = White.Core.Application.Launch(appPath);
  5. Assert.IsNotNull(приложение);
  6. varwindow = application.GetWindow( "MainWindow");
  7. var textBox = window.Get();
  8. var button = window.Get(SearchCriteria.ByText( "Щракнете за тест" ));
  9. бутон.Щракнете();
  10. Assert.AreEqual( "Habrahabr" , textBox.Text);
  11. >
* Този изходен код беше подчертан с инструмента за открояване на изходния код.

3. Тест за кодиран потребителски интерфейс на Visual Studio 2010

Предимства
недостатъци

Като цяло наборът от недостатъци е същият като този наUI Automation. Отделно е необходимо само да се подчертае фактът, че възможността за работа е само в определени версии2010 студия (Ultimate, Premium, Professional). Освен това, ако провеждането на тестове е възможно и в трите, тогава създаването на съответния тип елемент в проекта и стартирането на рекордера е възможно само във версиите Ultimate и Premium. И ако за вашите домашни проекти можете да изтеглите Ultimate версията от торенти, тогава за търговски проект, където говорим за лиценз за десетки разработчици, подобна стъпка може да срещне неразбиране от страна на висшите органи и счетоводните отдели.

Няма да предоставя тестовия код, защото е автоматично генериран и следователно не представлява особен интерес.

4. Test Complete и други подобни

Системи катоTest Completeмогат да бъдат групирани заедно. Няма да ги описвам подробно, т.к. Това е тема за отделна статия, ще подчертая само няколко точки. Основното им предимство е широк набор от приложения, липса на умения за програмиране и наличието на отделна система, която не изисква Visual Studio за създаване, съхранение и поддръжка на тестове. Недостатъците повтарят предишните решения - платено, третиране на тестваната система като „черна кутия“, без възможност за подигравка, плюсTest Completeима собствена среда за провеждане на тестове, така че използването на обичайния mstest няма да работи.

Нека направим нещо свое

Ако пишете потребителския интерфейс на вашето приложение в WPF, можете да използвате класаVisualTreeHelper, за да го тествате. Алгоритъмът е доста прост - стартираме нашето приложение в отделна нишка в тестовия метод, получаваме необходимия контрол чрезVisualTreeHelper, емулираме събития и четем стойности за asserts.

За по-удобно създаване на тестове, за себе си направих малък помощен клас, който опростява рутинните действия:

* Този изходен кодбеше подчертано с инструмента за открояване на изходния код.

var window = application.Get(x => x.MainWindow);

* Този изходен код беше подчертан с инструмента за открояване на изходния код.

var textBox = _mainWindow.FindChild((TextBox el) => el.Name == "SomeText" );

* Този изходен код беше подчертан с инструмента за открояване на изходния код.

  1. private void AssertRender( низ очаквано име на изображение, FrameworkElement elementForTest)
  2. var image = elementForTest.Render();
  3. var expectPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, expectImageName);
  4. if (!( File .Exists(expectPath) && File .ReadAllBytes(expectPath).SequenceEqual(image)))
  5. Файл .WriteAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "fail_" + expectImageName), изображение);
  6. хвърляне на ново AssertFailedException( низ .Format( "Елементът не е равен на изображението ''" , elementForTest.Get(x => x.Name), expectImageName));
  7. >
  8. >
  9. AssertRender( "button.png" , бутон);
* Този изходен код беше подчертан с инструмента за открояване на изходния код.

Достоинства
  • Бесплатность.
  • Не изисква инсталиране на допълнителни библиотек.
  • Позволява мокиране на части от системата.
Недостатки
  • Няма рекордера за тестове. Все пишем с ръце.
  • Поддержка на тестов целиком се полага на вашите плечи.
Примери на тестове за поставена задача

  1. [Метод на тестване]
  2. public void TestStartup()
  3. var application = UI.Run(() => new App < MainWindow = new MainWindow() >);
  4. var mainWindow = application.Get(x => x.MainWindow);
  5. var textBox = mainWindow.FindChild((TextBox el) => el.Name == "SomeTexBox" );
  6. Assert.IsNotNull(текстово поле);
  7. Assert.AreEqual( "123123123" , textBox.Get(x=> x.Text));
  8. application.Invoke(x => x.Shutdown());
  9. >
* Този изходен код беше подчертан с инструмента за открояване на изходния код.

  1. [Метод на тестване]
  2. public void TestFirstButtonClick()
  3. var application = UI.Run(() => new App < MainWindow = new MainWindow() >);
  4. var mainWindow = application.Get(x => x.MainWindow);
  5. var textBox = mainWindow.FindChild((TextBox el) => el.Name == "SomeTexBox" );
  6. var button = mainWindow.FindChild((Button el) => el.Content.Equals( "Щракнете за тест" ));
  7. button.Raise(ButtonBase.ClickEvent);
  8. Assert.AreEqual( "Habrahabr" , textBox.Get(x => x.Text));
  9. application.Invoke(x => x.Shutdown());
  10. >
* Този изходен код беше подчертан с инструмента за открояване на изходния код.

  1. [Метод на тестване]
  2. [HostType("Moles")]
  3. public void TestSecondButton()
  4. var application = UI.Run(() => new App < MainWindow = new MainWindow() >);
  5. var mainWindow = application.Get(x => x.MainWindow);
  6. var dateTimeExpect = нов DateTime (2011, 12, 08, 12, 30, 25);
  7. MDateTime.NowGet = () => dateTimeExpect;
  8. var button = mainWindow.FindChild((Button el) => el.Content.Equals( "Щракнете за тест 2" ));
  9. button.Raise(ButtonBase.ClickEvent);
  10. var textBox = mainWindow.FindChilds

().Първо();

  • Assert.AreEqual(dateTimeExpect.ToString(), textBox.Get(x => x.Text));
  • application.Invoke(x => x.Shutdown());
  • >
  • * Този изходен код беше подчертан с инструмента за открояване на изходния код.

    Тук аз просто замъкнах извикванетоDateTime.Nowс помощтаframeworkMoles, преглед на който можете да намерите например тук.

    Създаването на приложение и получаването на прозорец от него, разбира се, може да бъде включено в статичния конструктор на тестовия клас, за да се ускори времето за преминаване на тестовия пакет и да се опрости кодът им.

    Заключение

    И тук можете да получите грант за тестов период на Yandex.Cloud. Необходимо е само да въведете "Habr" в полето "секретна парола".