|
|
# Writing Tests
|
|
|
|
|
|
https://invent.kde.org/sdk/selenium-webdriver-at-spi/-/blob/master/examples/calculatortest.py
|
|
|
|
|
|
## Glossary
|
|
|
|
|
|
- Selenium: A testing system used mostly for weby stuff
|
|
|
- Appium: An extension to selenium making it more useful for app testing
|
|
|
- Driver: The server component of selenium
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
- https://invent.kde.org/sdk/selenium-webdriver-at-spi
|
|
|
- Install selenium-webdriver-at-spi dependencies with `pip3 install -r requirements.txt`
|
|
|
- Make sure ~/.local/bin is in your $PATH
|
|
|
- appstream://accerciser.desktop
|
|
|
|
|
|
## Test
|
|
|
|
|
|
You can write tests in any language you want. For the purposes of this guide we are going to use python in the hopes that most readers are familiar enough with that language.
|
|
|
|
|
|
```python
|
|
|
class SimpleCalculatorTests(unittest.TestCase):
|
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
suite = unittest.TestLoader().loadTestsFromTestCase(SimpleCalculatorTests)
|
|
|
unittest.TextTestRunner(verbosity=2).run(suite)
|
|
|
```
|
|
|
|
|
|
We start off by creating our test class.
|
|
|
|
|
|
```python
|
|
|
@classmethod
|
|
|
def setUpClass(self):
|
|
|
desired_caps = {}
|
|
|
# The app capability may be a command line or a desktop file id.
|
|
|
desired_caps["app"] = "org.kde.kcalc.desktop"
|
|
|
# Boilerplate, always the same
|
|
|
self.driver = webdriver.Remote(
|
|
|
command_executor='http://127.0.0.1:4723',
|
|
|
desired_capabilities=desired_caps)
|
|
|
# Set a timeout for waiting to find elements. If elements cannot be found
|
|
|
# in time we'll get a test failure. This should be somewhat long so as to
|
|
|
# not fall over when the system is under load, but also not too long that
|
|
|
# the test takes forever.
|
|
|
self.driver.implicitly_wait = 10
|
|
|
|
|
|
@classmethod
|
|
|
def tearDownClass(self):
|
|
|
# Make sure to terminate the driver again, lest it dangles.
|
|
|
self.driver.quit()
|
|
|
```
|
|
|
|
|
|
We'll also define some boilerplate setup logic. This will start the app org.kde.kcalc.desktop through its desktop file. You can also pass command lines to instead fork a process manually. For example you might start a Plasma applet with `"plasmawindowed org.kde.plasma.calculator"`.
|
|
|
|
|
|
Let's write our first test. A simple addition should do. To write selenium tests we need to tell the driver to find specific UI elements and interact with them (e.g. click them). There are a number of options for finding elements based on at-spi properties:
|
|
|
|
|
|
- name: `self.driver.find_element(by=AppiumBy.NAME, value="AC")`
|
|
|
- description: `self.driver.find_element(by='description', value="Result Display")`
|
|
|
- id (requires latest Qt Patch Set): `self.driver.find_element(by=AppiumBy.ID, value="QApplication.MainWindow#1.KCalculator.calc_display")`
|
|
|
- class name: `self.driver.find_element(by=AppiumBy.CLASS_NAME, value="[push button | AC]")`
|
|
|
|
|
|
To figure out what to actually look for we can look at at-spi directly. To do this we'll use the tool "accerciser". On the left hand side you can navigate the various accessible elements. On the right hand side you can inspect the element. The most pertinent tab here is 'Interface Viewer', it let's us find most of the locator types as well as inspect interaction options we have in the "Action" group as well as state assertion options in the "States" list view.
|
|
|
|
|
|
![Screenshot_20221125_133136-1](uploads/8d0bdc1f09cf0f173bd28e067fc9a9a5/Screenshot_20221125_133136-1.png)
|
|
|
|
|
|
Let's sketch out a simple addition test.
|
|
|
|
|
|
```python
|
|
|
def test_addition(self):
|
|
|
self.driver.find_element(by=AppiumBy.NAME, value="1").click()
|
|
|
self.driver.find_element(by=AppiumBy.NAME, value="+").click()
|
|
|
self.driver.find_element(by=AppiumBy.NAME, value="7").click()
|
|
|
self.driver.find_element(by=AppiumBy.NAME, value="=").click()
|
|
|
```
|
|
|
|
|
|
1+7=8. Easy enough. We'll have to replicate the interaction with the UI. We need to click the numbers and actions in order. For simplicity we'll find the elements by name, but be mindful that finding by name can easily be ambiguous (e.g. in kcalc's specific case the result display may have the same name as a button). When ambiguity is a possibility it's generally a better idea to use one of the other locator strategies.
|
|
|
|
|
|
```python
|
|
|
displaytext = self.driver.find_element(by='description', value="Result Display").text
|
|
|
self.assertEqual(displaytext, "8")
|
|
|
```
|
|
|
|
|
|
Lastly we'll find the result display element, obtain its text property, and assert it being 8. We now have our first test completed. The complete test can be found at https://invent.kde.org/sdk/selenium-webdriver-at-spi/-/blob/master/examples/calculatortest.py |