Skip to content

Commit 77bf873

Browse files
Merge pull request #1 from thiagojacinto/feat/running-tests-with-GHA
Feature: Running tests with GitHub Actions
2 parents 478b210 + 2eacba6 commit 77bf873

File tree

8 files changed

+203
-9
lines changed

8 files changed

+203
-9
lines changed

.github/workflows/run-tests.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Run Tests (parallel)
2+
3+
on: [workflow_dispatch]
4+
5+
jobs:
6+
test:
7+
name: Run tests
8+
runs-on: ubuntu-latest
9+
10+
container:
11+
image: python:3.11.7-alpine3.18
12+
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
browser: ['firefox', 'chrome', 'edge']
17+
18+
services:
19+
selenium:
20+
image: selenium/standalone-${{ matrix.browser }}:4.15.0-20231129
21+
options: --shm-size=2gb
22+
env:
23+
SE_NODE_OVERRIDE_MAX_SESSIONS: true
24+
SE_NODE_MAX_SESSIONS: 15
25+
SE_NODE_SESSION_TIMEOUT: 30
26+
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: actions/setup-python@v4
30+
with:
31+
python-version: "3.11.7"
32+
- run: pip install -r requirements-lock.txt
33+
- run: pytest -vv --use-browser remote
34+
env:
35+
SELENIUM_HOST: ${{ matrix.browser }}

Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
# shell colors
3+
COLOUR_GREEN=\033[0;32m
4+
COLOUR_RED=\033[0;31m
5+
COLOUR_BLUE=\033[0;34m
6+
COLOUR_END=\033[0m
7+
8+
se-docker-up: #starts the docker containers set by containers/compose.yaml file:
9+
@echo "$(COLOUR_GREEN)UP Docker containers set by compose file ...$(COLOUR_END)"
10+
docker compose -f containers/compose.yaml up -d
11+
12+
se-docker-down: #terminates the docker containers set by containers/compose.yaml file:
13+
@echo "$(COLOUR_RED)DOWN Docker containers set by compose file ...$(COLOUR_END)"
14+
docker compose -f containers/compose.yaml down
15+
16+
se-docker-run-tests: #Running tests for Firefox, Chrome and Edge browsers from docker containers
17+
@echo "$(COLOUR_BLUE)Running tests for Firefox, Chrome and Edge browsers from docker containers ...$(COLOUR_END)"
18+
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=chrome pytest -vx --use-browser remote & SELENIUM_HOST=firefox pytest -vx --use-browser remote & SELENIUM_HOST=edge pytest -vx --use-browser remote"
19+
20+
se-docker-run-tests-firefox: #Running tests for Firefox browser from docker containers
21+
@echo "$(COLOUR_BLUE)Running tests for Firefox browser from docker containers ...$(COLOUR_END)"
22+
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=firefox pytest -vx --use-browser remote"
23+
24+
se-docker-run-tests-chrome: #Running tests for Chrome browser from docker containers
25+
@echo "$(COLOUR_BLUE)Running tests for Chrome browser from docker containers ...$(COLOUR_END)"
26+
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=chrome pytest -vx --use-browser remote "
27+
28+
se-docker-run-tests-edge: #Running tests for Edge browser from docker containers
29+
@echo "$(COLOUR_BLUE)Running tests for Edge browser from docker containers ...$(COLOUR_END)"
30+
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=edge pytest -vx --use-browser remote"
31+
32+
33+
#: #########################################
34+
#: ############ Help - Makefile ############
35+
#: #########################################
36+
37+
help: # list all Makefile commands
38+
@echo "$(COLOUR_BLUE)These are all the avalaible commands ...$(COLOUR_END)"
39+
@echo ""
40+
@grep ': #' Makefile

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Automated tests using Python flavor of the Selenium framework.
1010
- [Expected output of test run](#expected-output-of-test-run)
1111
- [Selecting a specific browser](#selecting-a-specific-browser)
1212
- [Set a default browser to run a specific test](#set-a-default-browser-to-run-a-specific-test)
13+
- [Using `pytest` parametrize option to repeat test on different browsers](#using-pytest-parametrize-option-to-repeat-test-on-different-browsers)
14+
- [Using containers to run tests](#using-containers-to-run-tests)
1315

1416

1517
# How to install
@@ -87,4 +89,40 @@ def test_successfully_run_with_firefox(...):
8789
...
8890
```
8991

90-
The test above will be run using the Firefox browser and this mark takes precedence over the CLI argument `--use-browser`, if it is passed.
92+
The test above will be run using the Firefox browser and this mark takes precedence over the CLI argument `--use-browser`, if it is passed.
93+
94+
## Using `pytest` parametrize option to repeat test on different browsers
95+
96+
It's useful to use the powers of `pytest` framework to handle situations where the specific test case should be run against a variety of different browsers. A way to make it works it's by using `pytest.mark.parametrize` fixture, as the example below:
97+
98+
```python
99+
@pytest.mark.parametrize(
100+
(""), [
101+
pytest.param(id="default"),
102+
pytest.param(id="edge", marks=pytest.mark.FORCE_BROWSER("edge")),
103+
pytest.param(id="chrome", marks=pytest.mark.FORCE_BROWSER("chrome"))
104+
]
105+
)
106+
def test_successfully_run_with_edge_chrome_and_default_config(...):
107+
...
108+
```
109+
The way the example above works is by only applying:
110+
- an _ID_: to be displayed on the pytest output;
111+
- the custom `FORCE_BROWSER` mark: to force the use of the specified browser.
112+
113+
## Using containers to run tests
114+
115+
To rapidly set a development environment, using Docker to run containers is a valid alternative. Documentation for `docker-selenium` is set on the [project official repository](https://github.com/SeleniumHQ/docker-selenium) and it might be a good idea to take some time to read it.
116+
117+
The first steps are condensed on this [Makefile](Makefile), and you could start with the `make help` command to see the basics to: put containers UP or DOWN with Docker, then execute the tests on this infrastructure. All containers configuration are disposed on [compose.yaml](containers/compose.yaml)
118+
119+
```sh
120+
make help
121+
# > These are all the avalaible commands ...
122+
123+
make se-docker-up # starts the docker containers set by containers/compose.yaml file
124+
125+
make se-docker-run-tests #Running tests for Firefox, Chrome and Edge browsers from docker containers
126+
127+
make se-docker-down # terminates the docker containers set by containers/compose.yaml file
128+
```

conftest.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ def use_browser_option(request):
2626
"""Fixture to handle the use of argument --use-browser to select"""
2727

2828
force_browser = request.node.get_closest_marker("FORCE_BROWSER")
29+
selected_browser = request.config.getoption("--use-browser")
30+
2931
if force_browser is None:
30-
selected_browser = request.config.getoption("--use-browser")
3132
return selected_browser
33+
34+
elif selected_browser.lower() == "remote":
35+
pytest.skip(reason="Test to be run on specific browser locally")
3236

3337
return force_browser.args[0]
3438

containers/compose.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
version: '3'
2+
3+
services:
4+
5+
firefox:
6+
image: selenium/standalone-firefox:4.15.0-20231129
7+
shm_size: '2gb'
8+
ports:
9+
- 4444:4444 # Selenium service
10+
- 5900:5900 # VNC server
11+
- 7900:7900 # VNC browser client
12+
environment:
13+
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
14+
- SE_NODE_MAX_SESSIONS=15
15+
- SE_NODE_SESSION_TIMEOUT=30
16+
17+
chrome:
18+
image: selenium/standalone-chrome:4.15.0-20231129
19+
shm_size: '2gb'
20+
ports:
21+
- 4441:4444 # Selenium service
22+
- 5901:5900 # VNC server
23+
- 7901:7900 # VNC browser client
24+
environment:
25+
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
26+
- SE_NODE_MAX_SESSIONS=15
27+
- SE_NODE_SESSION_TIMEOUT=30
28+
29+
edge:
30+
image: selenium/standalone-edge:4.15.0-20231129
31+
shm_size: '2gb'
32+
ports:
33+
- 4442:4444 # Selenium service
34+
- 5902:5900 # VNC server
35+
- 7902:7900 # VNC browser client
36+
environment:
37+
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
38+
- SE_NODE_MAX_SESSIONS=15
39+
- SE_NODE_SESSION_TIMEOUT=30
40+
41+
python:
42+
image: python:3.11.7-alpine3.18
43+
volumes:
44+
- ./../:/test
45+
working_dir: /test
46+
command: sh -c "pip install -r requirements-lock.txt && sh"
47+
tty: true

tests/pages/BasePageObject.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
import os
2+
13
from selenium import webdriver
2-
from selenium.webdriver.firefox.options import Options
4+
from selenium.webdriver.firefox.options import Options as FirefoxOptions
5+
from selenium.webdriver.chrome.options import Options as ChromeOptions
6+
from selenium.webdriver.edge.options import Options as EdgeOptions
7+
from selenium.webdriver.safari.options import Options as SafariOptions
38

49

510
class BasePageObject:
@@ -16,25 +21,44 @@ def __init__(self, driver = None):
1621

1722
match driver:
1823
case None:
19-
new_driver_options = Options()
24+
new_driver_options = FirefoxOptions()
2025
new_driver_options.add_argument("-headless")
2126
self.driver = webdriver.Firefox(new_driver_options)
27+
case "remote":
28+
hostname = os.environ.get("SELENIUM_HOST", "selenium")
29+
30+
match hostname.lower():
31+
case "chrome":
32+
new_driver_options = ChromeOptions()
33+
case "firefox":
34+
new_driver_options = FirefoxOptions()
35+
case "edge":
36+
new_driver_options = EdgeOptions()
37+
case _:
38+
new_driver_options = FirefoxOptions()
39+
40+
self.driver = webdriver.Remote(
41+
command_executor="http://{}:4444".format(hostname),
42+
options=new_driver_options,
43+
keep_alive=True,
44+
)
45+
2246
case "firefox":
2347
self.driver = webdriver.Firefox()
2448
case "headless-firefox":
25-
new_driver_options = Options()
49+
new_driver_options = FirefoxOptions()
2650
new_driver_options.add_argument("-headless")
2751
self.driver = webdriver.Firefox(new_driver_options)
2852
case "chrome":
2953
self.driver = webdriver.Chrome()
3054
case "headless-chrome":
31-
new_driver_options = Options()
55+
new_driver_options = ChromeOptions()
3256
new_driver_options.add_argument("-headless")
3357
self.driver = webdriver.Chrome(new_driver_options)
3458
case "safari":
3559
self.driver = webdriver.Safari()
36-
case "headless-firefox":
37-
new_driver_options = Options()
60+
case "headless-safari":
61+
new_driver_options = SafariOptions()
3862
new_driver_options.add_argument("-headless")
3963
self.driver = webdriver.Firefox(new_driver_options)
4064
case _:

tests/test_ct005.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
from tests.pages.CheckoutInformationPage import CheckoutInformationPage
77
from tests.pages.CheckoutOverviewPage import CheckoutOverviewPage
88

9-
@pytest.mark.FORCE_BROWSER("firefox")
9+
@pytest.mark.parametrize(
10+
(""), [
11+
pytest.param(id="default"),
12+
pytest.param(id="firefox", marks=pytest.mark.FORCE_BROWSER("firefox"))
13+
]
14+
)
1015
def test_successfully_buy_a_product(start_with_one_product_added):
1116
cart_page = CartPage(driver = start_with_one_product_added.driver)
1217
cart_products_list = cart_page.get_cart_items()

tests/test_ct006.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
@pytest.mark.parametrize(
77
(""), [
8+
pytest.param(id="default"),
89
pytest.param(id="firefox", marks=pytest.mark.FORCE_BROWSER("firefox")),
910
pytest.param(id="chrome", marks=pytest.mark.FORCE_BROWSER("chrome")),
1011
pytest.param(id="safari", marks=pytest.mark.FORCE_BROWSER("safari"))

0 commit comments

Comments
 (0)