The GitHub Actions matrix strategy is a powerful feature that allows you to run the same job across multiple combinations of variables—like environments, operating systems, or software versions—without duplicating code. It's ideal for scenarios such as:
- Testing across different browser environments
- Running code against multiple versions of a language
- Deploying across multiple platforms (e.g., Android and iOS)
By using a strategy.matrix
section in your workflow, you can define all the variations you want to test or execute. GitHub will then automatically create a job for each combination. This not only helps keep workflows clean and scalable, but also provides a clear view of which combinations succeed or fail.
For example:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [16, 18]
This configuration will run four jobs in parallel:
- Ubuntu + Node 16
- Ubuntu + Node 18
- Windows + Node 16
- Windows + Node 18
Learn more in the official GitHub Docs
Cross-Browser UI Testing with GitHub Actions Matrix Strategy
When building UI automation workflows, one of the most common challenges is ensuring consistent functionality across different browsers. Initially, I handled this by creating separate GitHub Actions YAML files for each browser:
ui_chrome_pytest.yml
ui_firefox_pytest.yml
Each file had almost identical steps, with only minor differences:
🔍 Key Differences in the Old Files
ui_chrome_pytest.yml
manually installs Google Chrome and ChromeDriver.
ui_firefox_pytest.yml
uses the browser-actions/setup-firefox
action for Firefox and GeckoDriver.
The pytest command uses --browser chrome
in one and --browser firefox
in the other.
Both upload the same artifacts, but the file names are not browser-specific (they would overwrite each other if run together).
This setup worked, but had several downsides:
- ❌ Duplication of logic: Every change had to be applied in multiple files.
- ❌ Error-prone: It’s easy to forget updating one file.
- ❌ Scalability issues: Adding more browsers or platforms meant creating even more nearly-identical files.
The Solution: Matrix Strategy
So, to eliminate the duplication and better manage different browser setups, I leveraged the GitHub Actions matrix strategy. This approach allows each browser to be tested in parallel, using a single, unified workflow file.
To solve this, I created a new GitHub Actions workflow using a matrix strategy that dynamically switches between browsers:
ui_cross_browser_test.yml
name: UI Automation Tests
on:
pull_request:
branches:
- main
jobs:
build_and_test:
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chrome, firefox] # Matrix used to define browsers to test
steps:
- name: Checkout resume repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies and build
run: |
yarn install
yarn build
- name: Start the application
run: |
yarn start &
env:
PORT: 3000
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
# Chrome-specific installation
- name: Install Chrome and ChromeDriver
if: matrix.browser == 'chrome'
run: |
sudo apt update
sudo apt install -y wget gnupg
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install -y ./google-chrome-stable_current_amd64.deb
CHROMEDRIVER_VERSION=$(curl -sS https://chromedriver.storage.googleapis.com/LATEST_RELEASE)
wget https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip
unzip chromedriver_linux64.zip
sudo mv chromedriver /usr/local/bin/chromedriver
sudo chmod +x /usr/local/bin/chromedriver
# Firefox-specific setup using reusable action
- name: Set up Firefox and GeckoDriver
if: matrix.browser == 'firefox'
uses: browser-actions/setup-firefox@latest
- name: Clone the test repository
run: |
git clone https://github.com/serhatozdursun/serhatozdursun-ui-tests.git
env:
GITHUB_TOKEN: ${{ secrets.UI_TEST_TOKEN }}
- name: Install Python dependencies
working-directory: serhatozdursun-ui-tests
run: |
pip install -r requirements.txt
# Matrix-aware pytest command
- name: Run tests on ${{ matrix.browser }}
working-directory: serhatozdursun-ui-tests
id: run_tests
run: |
pytest --browser ${{ matrix.browser }} --base_url http://localhost:3000 --html=reports/html/report.html --junitxml=reports/report.xml
- name: Upload HTML report
if: always()
uses: actions/upload-artifact@v4
with:
name: html-report-${{ matrix.browser }}
path: serhatozdursun-ui-tests/reports/html
retention-days: 5
continue-on-error: true
- name: Upload XML report
if: always()
uses: actions/upload-artifact@v4
with:
name: xml-report-${{ matrix.browser }}
path: serhatozdursun-ui-tests/reports/report.xml
retention-days: 5
continue-on-error: true
Before vs After
Approach | Pros | Cons |
---|---|---|
Separate files | Easy to isolate logic | Repetitive, hard to scale |
Matrix strategy | Clean, DRY, scalable, browser-aware | Slightly more complex YAML logic |
Final Thoughts
Using GitHub Actions' matrix strategy is a game-changer for cross-browser testing. If you're managing separate workflows per browser, consolidating them will make your CI setup much more efficient.
Stay sharp, test smart! 🚀🧪
Connect with me on GitHub: serhatozdursun.com