Photo by Mateus Campos Felipe
Scarlet ibis (Guará)
The scarlet ibis, sometimes called red ibis (Eudocimus ruber), is a species of ibis in the bird family Threskiornithidae. It inhabits tropical South America and part of the Caribbean. In form, it resembles most of the other twenty-seven extant species of ibis, but its remarkably brilliant scarlet coloration makes it unmistakable. It is one of the two national birds of Trinidad and Tobago, and its Tupi–Guarani name, guará, is part of the name of several municipalities along the coast of Brazil.
The pattern
Guará is the Python implementation of the design pattern Page Transactions. It is more of a programming pattern than a tool. As a pattern, it can be bound to any driver other than Selenium including the ones used to Linux, Window and Mobile automation.
The intent of this pattern is to simplify test automation. It was inspired by Page Objects, App Actions, and Screenplay. Page Transactions focus on the operations (transactions) a user can perform on an application, such as Login, Logout, or Submit Forms.
This initiative was born to improve the readability, maintainability and flexibility of automation testing code without the need for new automation tools or the necessity of many abstractions to build human-readable statements. Another concern was to avoid binding the framework to specific automation tools, like Selenium, leaving the testers free to choose their preferred ones. No new plugin nor new knowledge is required to start using Guará with Helium, Dogtail, RPA Python, Playwright or whatever tool you like or need.
It is worth to say again:
Guará is the Python implementation of the design pattern Page Transactions. It is more of a programming pattern than a tool.
Guará leverages the Command Pattern (GoF) to group user interactions, like pressing buttons or filling texts, into transactions. Despite I’m calling it a framework, it is not a new tool.
Instead of focusing on UI elements like buttons, check boxes or text areas, it emphasizes the user journey. The complexity is abstracted into these transactions, making the test statements feels like plain English. Testers also have the flexibility to extend assertions to custom ones that are not provided by the framework.
The Framework in action
This simple implementation mimics the user switching languages in a Web Page:
<span>from</span> <span>selenium</span> <span>import</span> <span>webdriver</span><span>from</span> <span>guara.transaction</span> <span>import</span> <span>Application</span><span>from</span> <span>guara</span> <span>import</span> <span>it</span><span>,</span> <span>setup</span><span>import</span> <span>home</span><span>def</span> <span>test_language_switch</span><span>():</span><span>app</span> <span>=</span> <span>Application</span><span>(</span><span>webdriver</span><span>.</span><span>Chrome</span><span>())</span><span># Open the application </span> <span>app</span><span>.</span><span>at</span><span>(</span><span>setup</span><span>.</span><span>OpenApp</span><span>,</span> <span>url</span><span>=</span><span>"</span><span>https://example.com/</span><span>"</span><span>)</span><span># Change language and assert </span> <span>app</span><span>.</span><span>at</span><span>(</span><span>home</span><span>.</span><span>ChangeToPortuguese</span><span>).</span><span>asserts</span><span>(</span><span>it</span><span>.</span><span>IsEqualTo</span><span>,</span> <span>"</span><span>Conteúdo em Português</span><span>"</span><span>)</span><span>app</span><span>.</span><span>at</span><span>(</span><span>home</span><span>.</span><span>ChangeToEnglish</span><span>).</span><span>asserts</span><span>(</span><span>it</span><span>.</span><span>IsEqualTo</span><span>,</span> <span>"</span><span>Content in English</span><span>"</span><span>)</span><span># Close the application </span> <span>app</span><span>.</span><span>at</span><span>(</span><span>setup</span><span>.</span><span>CloseApp</span><span>)</span><span>from</span> <span>selenium</span> <span>import</span> <span>webdriver</span> <span>from</span> <span>guara.transaction</span> <span>import</span> <span>Application</span> <span>from</span> <span>guara</span> <span>import</span> <span>it</span><span>,</span> <span>setup</span> <span>import</span> <span>home</span> <span>def</span> <span>test_language_switch</span><span>():</span> <span>app</span> <span>=</span> <span>Application</span><span>(</span><span>webdriver</span><span>.</span><span>Chrome</span><span>())</span> <span># Open the application </span> <span>app</span><span>.</span><span>at</span><span>(</span><span>setup</span><span>.</span><span>OpenApp</span><span>,</span> <span>url</span><span>=</span><span>"</span><span>https://example.com/</span><span>"</span><span>)</span> <span># Change language and assert </span> <span>app</span><span>.</span><span>at</span><span>(</span><span>home</span><span>.</span><span>ChangeToPortuguese</span><span>).</span><span>asserts</span><span>(</span><span>it</span><span>.</span><span>IsEqualTo</span><span>,</span> <span>"</span><span>Conteúdo em Português</span><span>"</span><span>)</span> <span>app</span><span>.</span><span>at</span><span>(</span><span>home</span><span>.</span><span>ChangeToEnglish</span><span>).</span><span>asserts</span><span>(</span><span>it</span><span>.</span><span>IsEqualTo</span><span>,</span> <span>"</span><span>Content in English</span><span>"</span><span>)</span> <span># Close the application </span> <span>app</span><span>.</span><span>at</span><span>(</span><span>setup</span><span>.</span><span>CloseApp</span><span>)</span>from selenium import webdriver from guara.transaction import Application from guara import it, setup import home def test_language_switch(): app = Application(webdriver.Chrome()) # Open the application app.at(setup.OpenApp, url="https://example.com/") # Change language and assert app.at(home.ChangeToPortuguese).asserts(it.IsEqualTo, "Conteúdo em Português") app.at(home.ChangeToEnglish).asserts(it.IsEqualTo, "Content in English") # Close the application app.at(setup.CloseApp)
Enter fullscreen mode Exit fullscreen mode
Each user transaction is grouped into its own class (e.g., ChangeToPortuguese) which inherits from AbstractTransaction. The tester just has to override the do method and the framework does the work.
<span>from</span> <span>guara.transaction</span> <span>import</span> <span>AbstractTransaction</span><span>class</span> <span>ChangeToPortuguese</span><span>(</span><span>AbstractTransaction</span><span>):</span><span>def</span> <span>do</span><span>(</span><span>self</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span><span>self</span><span>.</span><span>_driver</span><span>.</span><span>find_element</span><span>(</span><span>By</span><span>.</span><span>CSS_SELECTOR</span><span>,</span> <span>"</span><span>.btn-pt</span><span>"</span><span>).</span><span>click</span><span>()</span><span>return</span> <span>self</span><span>.</span><span>_driver</span><span>.</span><span>find_element</span><span>(</span><span>By</span><span>.</span><span>CSS_SELECTOR</span><span>,</span> <span>"</span><span>.content</span><span>"</span><span>).</span><span>text</span><span>from</span> <span>guara.transaction</span> <span>import</span> <span>AbstractTransaction</span> <span>class</span> <span>ChangeToPortuguese</span><span>(</span><span>AbstractTransaction</span><span>):</span> <span>def</span> <span>do</span><span>(</span><span>self</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span> <span>self</span><span>.</span><span>_driver</span><span>.</span><span>find_element</span><span>(</span><span>By</span><span>.</span><span>CSS_SELECTOR</span><span>,</span> <span>"</span><span>.btn-pt</span><span>"</span><span>).</span><span>click</span><span>()</span> <span>return</span> <span>self</span><span>.</span><span>_driver</span><span>.</span><span>find_element</span><span>(</span><span>By</span><span>.</span><span>CSS_SELECTOR</span><span>,</span> <span>"</span><span>.content</span><span>"</span><span>).</span><span>text</span>from guara.transaction import AbstractTransaction class ChangeToPortuguese(AbstractTransaction): def do(self, **kwargs): self._driver.find_element(By.CSS_SELECTOR, ".btn-pt").click() return self._driver.find_element(By.CSS_SELECTOR, ".content").text
Enter fullscreen mode Exit fullscreen mode
The tester can check the the transactions and assertions in the logs after run the tests:
test_demo.py::test_language_switch2025-01-24 21:07:10 INFO Transaction: setup.OpenApp2025-01-24 21:07:10 INFO url: https://example.com/2025-01-24 21:07:14 INFO Transaction: home.ChangeToPortuguese2025-01-24 21:07:14 INFO Assertion: IsEqualTo2025-01-24 21:07:14 INFO Actual Data: Conteúdo em Português2025-01-24 21:07:14 INFO Expected: Conteúdo em Português2025-01-24 21:07:14 INFO Transaction: home.ChangeToEnglish2025-01-24 21:07:14 INFO Assertion: IsEqualTo2025-01-24 21:07:14 INFO Actual Data: Content <span>in </span>English2025-01-24 21:07:14 INFO Expected: Content <span>in </span>English2025-01-24 21:07:14 INFO Transaction: setup.CloseApptest_demo.py::test_language_switch 2025-01-24 21:07:10 INFO Transaction: setup.OpenApp 2025-01-24 21:07:10 INFO url: https://example.com/ 2025-01-24 21:07:14 INFO Transaction: home.ChangeToPortuguese 2025-01-24 21:07:14 INFO Assertion: IsEqualTo 2025-01-24 21:07:14 INFO Actual Data: Conteúdo em Português 2025-01-24 21:07:14 INFO Expected: Conteúdo em Português 2025-01-24 21:07:14 INFO Transaction: home.ChangeToEnglish 2025-01-24 21:07:14 INFO Assertion: IsEqualTo 2025-01-24 21:07:14 INFO Actual Data: Content <span>in </span>English 2025-01-24 21:07:14 INFO Expected: Content <span>in </span>English 2025-01-24 21:07:14 INFO Transaction: setup.CloseApptest_demo.py::test_language_switch 2025-01-24 21:07:10 INFO Transaction: setup.OpenApp 2025-01-24 21:07:10 INFO url: https://example.com/ 2025-01-24 21:07:14 INFO Transaction: home.ChangeToPortuguese 2025-01-24 21:07:14 INFO Assertion: IsEqualTo 2025-01-24 21:07:14 INFO Actual Data: Conteúdo em Português 2025-01-24 21:07:14 INFO Expected: Conteúdo em Português 2025-01-24 21:07:14 INFO Transaction: home.ChangeToEnglish 2025-01-24 21:07:14 INFO Assertion: IsEqualTo 2025-01-24 21:07:14 INFO Actual Data: Content in English 2025-01-24 21:07:14 INFO Expected: Content in English 2025-01-24 21:07:14 INFO Transaction: setup.CloseApp
Enter fullscreen mode Exit fullscreen mode
The tester can also use fixtures like setup and tear down to initiate and finish the tests. Remember, it is not a new tool, so you can use pytest or unittesting features without any problem.
Why use Guará?
Each class represents a complete user transaction, improving code re-usability. Also, the code is written in plain English, making it easier for non-technical collaborators to review and contribute.
Custom assertions can be created and shared by testers. Additionally, Guará can be integrated with any non-Selenium tool.
Page Transactions can automate REST APIs, unit tests, desktop, and mobile tests. As a side effect of the Command Pattern, the framework can even be used in product development.
Using Guará
Setting up Guará is simple:
- Install Guará with the command
pip install guara
-
Build your transactions using the AbstractTransaction class.
-
Invoke the transactions using the Application runner and its methods at and asserts.
-
Execute tests with detailed logging with Pytest:
python -m pytest -o log_cli=1 --log-cli-level=INFO
For more examples, check out the tutorial.
Conclusion
Guará is a new way testers can organize their code making it simple to read, maintain and integrate with any automation driver. It improves the collaboration between testers and non-technical members. Testers can also extend it building and sharing new assertions.
原文链接:Page Transactions as a new way to organize your testing automation
暂无评论内容