Skip to content

Feature: Running tests with GitHub Actions #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/run-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Run Tests (parallel)

on: [workflow_dispatch]

jobs:
test:
name: Run tests
runs-on: ubuntu-latest

container:
image: python:3.11.7-alpine3.18

strategy:
fail-fast: false
matrix:
browser: ['firefox', 'chrome', 'edge']

services:
selenium:
image: selenium/standalone-${{ matrix.browser }}:4.15.0-20231129
options: --shm-size=2gb
env:
SE_NODE_OVERRIDE_MAX_SESSIONS: true
SE_NODE_MAX_SESSIONS: 15
SE_NODE_SESSION_TIMEOUT: 30

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.11.7"
- run: pip install -r requirements-lock.txt
- run: pytest -vv --use-browser remote
env:
SELENIUM_HOST: ${{ matrix.browser }}
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

# shell colors
COLOUR_GREEN=\033[0;32m
COLOUR_RED=\033[0;31m
COLOUR_BLUE=\033[0;34m
COLOUR_END=\033[0m

se-docker-up: #starts the docker containers set by containers/compose.yaml file:
@echo "$(COLOUR_GREEN)UP Docker containers set by compose file ...$(COLOUR_END)"
docker compose -f containers/compose.yaml up -d

se-docker-down: #terminates the docker containers set by containers/compose.yaml file:
@echo "$(COLOUR_RED)DOWN Docker containers set by compose file ...$(COLOUR_END)"
docker compose -f containers/compose.yaml down

se-docker-run-tests: #Running tests for Firefox, Chrome and Edge browsers from docker containers
@echo "$(COLOUR_BLUE)Running tests for Firefox, Chrome and Edge browsers from docker containers ...$(COLOUR_END)"
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"

se-docker-run-tests-firefox: #Running tests for Firefox browser from docker containers
@echo "$(COLOUR_BLUE)Running tests for Firefox browser from docker containers ...$(COLOUR_END)"
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=firefox pytest -vx --use-browser remote"

se-docker-run-tests-chrome: #Running tests for Chrome browser from docker containers
@echo "$(COLOUR_BLUE)Running tests for Chrome browser from docker containers ...$(COLOUR_END)"
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=chrome pytest -vx --use-browser remote "

se-docker-run-tests-edge: #Running tests for Edge browser from docker containers
@echo "$(COLOUR_BLUE)Running tests for Edge browser from docker containers ...$(COLOUR_END)"
docker compose -f containers/compose.yaml exec python sh -c "SELENIUM_HOST=edge pytest -vx --use-browser remote"


#: #########################################
#: ############ Help - Makefile ############
#: #########################################

help: # list all Makefile commands
@echo "$(COLOUR_BLUE)These are all the avalaible commands ...$(COLOUR_END)"
@echo ""
@grep ': #' Makefile
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Automated tests using Python flavor of the Selenium framework.
- [Expected output of test run](#expected-output-of-test-run)
- [Selecting a specific browser](#selecting-a-specific-browser)
- [Set a default browser to run a specific test](#set-a-default-browser-to-run-a-specific-test)
- [Using `pytest` parametrize option to repeat test on different browsers](#using-pytest-parametrize-option-to-repeat-test-on-different-browsers)
- [Using containers to run tests](#using-containers-to-run-tests)


# How to install
Expand Down Expand Up @@ -87,4 +89,40 @@ def test_successfully_run_with_firefox(...):
...
```

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.
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.

## Using `pytest` parametrize option to repeat test on different browsers

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:

```python
@pytest.mark.parametrize(
(""), [
pytest.param(id="default"),
pytest.param(id="edge", marks=pytest.mark.FORCE_BROWSER("edge")),
pytest.param(id="chrome", marks=pytest.mark.FORCE_BROWSER("chrome"))
]
)
def test_successfully_run_with_edge_chrome_and_default_config(...):
...
```
The way the example above works is by only applying:
- an _ID_: to be displayed on the pytest output;
- the custom `FORCE_BROWSER` mark: to force the use of the specified browser.

## Using containers to run tests

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.

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)

```sh
make help
# > These are all the avalaible commands ...

make se-docker-up # starts the docker containers set by containers/compose.yaml file

make se-docker-run-tests #Running tests for Firefox, Chrome and Edge browsers from docker containers

make se-docker-down # terminates the docker containers set by containers/compose.yaml file
```
6 changes: 5 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ def use_browser_option(request):
"""Fixture to handle the use of argument --use-browser to select"""

force_browser = request.node.get_closest_marker("FORCE_BROWSER")
selected_browser = request.config.getoption("--use-browser")

if force_browser is None:
selected_browser = request.config.getoption("--use-browser")
return selected_browser

elif selected_browser.lower() == "remote":
pytest.skip(reason="Test to be run on specific browser locally")

return force_browser.args[0]

Expand Down
47 changes: 47 additions & 0 deletions containers/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
version: '3'

services:

firefox:
image: selenium/standalone-firefox:4.15.0-20231129
shm_size: '2gb'
ports:
- 4444:4444 # Selenium service
- 5900:5900 # VNC server
- 7900:7900 # VNC browser client
environment:
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
- SE_NODE_MAX_SESSIONS=15
- SE_NODE_SESSION_TIMEOUT=30

chrome:
image: selenium/standalone-chrome:4.15.0-20231129
shm_size: '2gb'
ports:
- 4441:4444 # Selenium service
- 5901:5900 # VNC server
- 7901:7900 # VNC browser client
environment:
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
- SE_NODE_MAX_SESSIONS=15
- SE_NODE_SESSION_TIMEOUT=30

edge:
image: selenium/standalone-edge:4.15.0-20231129
shm_size: '2gb'
ports:
- 4442:4444 # Selenium service
- 5902:5900 # VNC server
- 7902:7900 # VNC browser client
environment:
- SE_NODE_OVERRIDE_MAX_SESSIONS=true
- SE_NODE_MAX_SESSIONS=15
- SE_NODE_SESSION_TIMEOUT=30

python:
image: python:3.11.7-alpine3.18
volumes:
- ./../:/test
working_dir: /test
command: sh -c "pip install -r requirements-lock.txt && sh"
tty: true
36 changes: 30 additions & 6 deletions tests/pages/BasePageObject.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import os

from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.edge.options import Options as EdgeOptions
from selenium.webdriver.safari.options import Options as SafariOptions


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

match driver:
case None:
new_driver_options = Options()
new_driver_options = FirefoxOptions()
new_driver_options.add_argument("-headless")
self.driver = webdriver.Firefox(new_driver_options)
case "remote":
hostname = os.environ.get("SELENIUM_HOST", "selenium")

match hostname.lower():
case "chrome":
new_driver_options = ChromeOptions()
case "firefox":
new_driver_options = FirefoxOptions()
case "edge":
new_driver_options = EdgeOptions()
case _:
new_driver_options = FirefoxOptions()

self.driver = webdriver.Remote(
command_executor="http://{}:4444".format(hostname),
options=new_driver_options,
keep_alive=True,
)

case "firefox":
self.driver = webdriver.Firefox()
case "headless-firefox":
new_driver_options = Options()
new_driver_options = FirefoxOptions()
new_driver_options.add_argument("-headless")
self.driver = webdriver.Firefox(new_driver_options)
case "chrome":
self.driver = webdriver.Chrome()
case "headless-chrome":
new_driver_options = Options()
new_driver_options = ChromeOptions()
new_driver_options.add_argument("-headless")
self.driver = webdriver.Chrome(new_driver_options)
case "safari":
self.driver = webdriver.Safari()
case "headless-firefox":
new_driver_options = Options()
case "headless-safari":
new_driver_options = SafariOptions()
new_driver_options.add_argument("-headless")
self.driver = webdriver.Firefox(new_driver_options)
case _:
Expand Down
7 changes: 6 additions & 1 deletion tests/test_ct005.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
from tests.pages.CheckoutInformationPage import CheckoutInformationPage
from tests.pages.CheckoutOverviewPage import CheckoutOverviewPage

@pytest.mark.FORCE_BROWSER("firefox")
@pytest.mark.parametrize(
(""), [
pytest.param(id="default"),
pytest.param(id="firefox", marks=pytest.mark.FORCE_BROWSER("firefox"))
]
)
def test_successfully_buy_a_product(start_with_one_product_added):
cart_page = CartPage(driver = start_with_one_product_added.driver)
cart_products_list = cart_page.get_cart_items()
Expand Down
1 change: 1 addition & 0 deletions tests/test_ct006.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

@pytest.mark.parametrize(
(""), [
pytest.param(id="default"),
pytest.param(id="firefox", marks=pytest.mark.FORCE_BROWSER("firefox")),
pytest.param(id="chrome", marks=pytest.mark.FORCE_BROWSER("chrome")),
pytest.param(id="safari", marks=pytest.mark.FORCE_BROWSER("safari"))
Expand Down