📜 ⬆️ ⬇️

Winium.Desktop: Selenium for desktop applications under Windows


Hi, my name is Gleb, and I do test automation in 2GIS. More than a year ago, I wrote about our Cruciatus tool - with its help, we are testing UI desktop applications under Windows.

Cruciatus perfectly solves the problem of access to controls, but tests are written strictly in C #. This makes it difficult to fumble knowledge and experience between testers for different platforms: mobile, web and desktop.

We saw the solution in Selenium - perhaps the most famous tool for test automation. In this article, I will tell you how we crossed Cruciatus and Selenium and how to test the Windows interface of a desktop application using familiar Selenium bindings.

Why not enough Cruciatus


Almost all the teams that develop the internal products of 2GIS used Cruciatus. And each of these commands offered improvements for the tool. In order to please everyone, we reworked the logic of Cruciatus to the extent of backward compatibility. It was painful, but helpful.
')
We also abandoned the CodedUI Mouse & Keyboard classes to remove dependency on the libraries that came with VisualStudio. So they learned how to build a project on public CI servers like AppVeyor .

As a result, we have made a convenient and self-sufficient tool that solves all our tasks in accessing elements of desktop applications under Windows. But at the same time, Cruciatus has one serious limitation left - the C # dictatorship.

How to come to Selenium


Selenium is a set of tools and libraries for automating application testing in browsers. The heart of the Selenium project can be considered Json Wire Protocol (JSWP) - a single REST-protocol for interaction between tests and the application under test.

Advantages of a single protocol:

We decided to use these advantages in automating the testing of desktop applications, just as we use them for the web.

What is Winium.Desktop


To get away from the dictatorship of C #, we wrote a wrapper for Selenium compatible with Cruciatus. In parallel, the company created the same Selenium-compatible tool for autotests, but for mobile Windows applications. We combined these developments under the common name Winium, and our tool was called Winium.Desktop.

In essence, Winium.Desktop is an http client. It implements the JSWP protocol and uses Cruciatus to work with user interface elements. In fact, this is a WebDriver implementation for desktop applications under Windows.

With Winium.Desktop, we use familiar Selenium bindings to test desktop applications for Windows.

How to work with Winium.Desktop


To work with Winium.Desktop, download the latest driver release from github and run it as an administrator. This is not a mandatory condition, but otherwise, sooner or later you will come across Access denied from either the operating system or the application.

All is ready. Now take your favorite language, your favorite IDE and write tests the same way you would for a web application. And if you are not familiar with Selenium, read any documentation. We recommend starting with Selenium Python Bindings .

The only difference from web application testing is: to find out element locators, use tools like UISpy or UI Automation Verify. Let's talk more about them further.

When run the tests, do not touch the mouse and keyboard: the cursor will move, the focus will change, and automation will not happen.

What can a driver


When implementing the Json Wire Protocol, we relied on two drafts of the protocol used by WebDriver: the JsonWireProtocol and the more recent webdriver-spec .

Now we have implemented most of the most popular teams.

Full list
TeamRequest
New sessionPOST / session
Find elementPOST / session /: sessionId / element
FindChildElementPOST / session /: sessionId / element /: id / element
ClickElementPOST / session /: sessionId / element /: id / click
SendKeysToElementPOST / session /: sessionId / element /: id / value
GetElementTextGET / session /: sessionId / element /: id / text
GetElementAttributeGET / session /: sessionId / element /: id / attribute /: name
QuitDELETE / session /: sessionId
ClearelementPOST / session /: sessionId / element /: id / clear
CloseDELETE / session /: sessionId / window
ElementmentalsGET / session /: sessionId / element /: id / equals /: other
ExecuteScriptPOST / session /: sessionId / execute
FindChildElementsPOST / session /: sessionId / element /: id / elements
FindElementsPOST / session /: sessionId / elements
GetActiveElementPOST / session /: sessionId / element / active
GetElementSizeGET / session /: sessionId / element /: id / size
ImplicitlyWaitPOST / session /: sessionId / timeouts / implicit_wait
IsElementDisplayedGET / session /: sessionId / element /: id / displayed
IsElementEnabledGET / session /: sessionId / element /: id / enabled
IsElementSelectedGET / session /: sessionId / element /: id / selected
MouseclickPOST / session /: sessionId / click
MouseDoubleClickPOST / session /: sessionId / doubleclick
MouseMoveToPOST / session /: sessionId / moveto
ScreenshotGET / session /: sessionId / screenshot
SendKeysToActiveElementPOST / session /: sessionId / keys
StatusGET / status
SubmitElementPOST / session /: sessionId / element /: id / submit


An example of using the most simple commands (Python):
  1. Run the application with the NewSession command when creating the driver:
    driver = webdriver.Remote( command_executor='http://localhost:9999', desired_capabilities={ "app": r"C:/windows/system32/calc.exe" }) 
  2. Find the window of the application under test with the FindElement command:
     window = driver.find_element_by_class_name('CalcFrame') 
  3. Find an element in the window with the FindChildElement command:
     result_field = window.find_element_by_id('150') 
  4. Get the property of an item with the GetElementAttribute command:
     result_field.get_attribute('Name') 
  5. Close the application with the Quit command:
     driver.quit() 

The same, only C #:
  1.  var dc = new DesiredCapabilities(); dc.SetCapability("app", @"C:/windows/system32/calc.exe"); var driver = new RemoteWebDriver(new Uri("http://localhost:9999"), dc); var window = driver.FindElementByClassName("CalcFrame"); resultField = window.FindElement(By.Id("150")); resultField.GetAttribute("Name"); driver.Quit(); 

Read more about supported commands on the wiki in the project repository.

Work with elements


To control the elements in tests, these elements must first be found. Elements are searched by locators - properties that uniquely identify elements.

To find out element locators, use UISpy , its newer version of Inspect or UIAVerify . The last two are installed with VisualStudio and are located in the “% PROGRAMFILES (X86)% \ Windows Kits \ 8.1 \ bin \” directory (a difference in the Windows Kits version is possible).

Run any of these tools preferably from the administrator.
We recommend using UIAVerify. In our opinion, it is the most productive and convenient.

Although Cruciatus can search for items by any property from the AutomationElementIdentifiers class, Winium.Desktop supports only three search strategies (such as locators):

The root element in the search is the desktop. We recommend that you first find the window of the application being tested (FindElement) and only then the elements inside it (FindChildElement).

If you need to expand possible search strategies, please contact us or immediately create a new issue .

Example. Code that writes code


 from selenium import webdriver from selenium.webdriver import ActionChains import time driver = webdriver.Remote( command_executor='http://localhost:9999', desired_capabilities={ 'app': r'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe' }) window = driver.find_element_by_id('VisualStudioMainWindow') menu_bar = window.find_element_by_id('MenuBar') menu_bar.click() menu_bar.find_element_by_name('File').click() menu_bar.find_element_by_name('New').click() menu_bar.find_element_by_name('Project...').click() project_name = 'SpecialForHabrahabr-' + str(time.time()) new_project_win = window.find_element_by_name('New Project') new_project_win.find_element_by_id('Windows Desktop').click() new_project_win.find_element_by_name('Console Application').click() new_project_win.find_element_by_id('txt_Name').send_keys(project_name) new_project_win.find_element_by_id('btn_OK').click() text_view = window.find_element_by_id('WpfTextView') text_view.send_keys('using System;{ENTER}{ENTER}') actions = ActionChains(driver) actions.send_keys('namespace Habrahabr{ENTER}') actions.send_keys('{{}{ENTER}') actions.send_keys('class Program{ENTER}') actions.send_keys('{{}{ENTER}') actions.send_keys('static void Main{(}string{[}{]} args{)}{ENTER}') actions.send_keys('{{}{ENTER}') actions.send_keys('Console.WriteLine{(}\"Hello Habrahabr\"{)};') actions.send_keys('^{F5}') actions.perform() 


Continuous Integration for Winium.Desktop tests


In the CI project, tests managed by the Winium.Desktop driver are included in the standard way. However, they require real or virtual machine. When setting up such a machine, follow a few formalities.

First, the system requires a so-called active desktop. It exists on your computer or in RDP connection. And the window of this connection can not be minimized. To automatically create an active desktop, use Autologon .

Secondly, the active desktop must be kept active. To do this, set up the power supply on the machine (from under the user for whom Autologon is configured). Cancel the display off and sleep. If you are using an RDP connection, restart the machine upon completion. This will restore the active desktop. To peek at test execution, use System Center App Controller or VNC .

Thirdly, your build server agent must work as a process, not as a service. This limitation is due to the fact that on Windows the service does not have rights to run the user interface of the application.

Total: configure Autologon, keep the desktop active and start the build server agent as a process.

Conclusion


The Winium.Desktop project allowed us to blur the line between automating user interface testing of web and desktop applications.

Now testers freely exchange experience and practices of using Selenium. And autotests, written for these very different platforms, run in the same cloud infrastructure, built on the basis of Selenium-Grid.

Once again the link to the repository and other opensource 2GIS products .

Source: https://habr.com/ru/post/263347/


All Articles