There is nothing quite as nerve-wracking as hitting ‘Merge’ on a critical PR, only to realize five minutes later that you broke the production build because you forgot to run the test suite locally. I’ve been there more times than I’d like to admit. That’s why I transitioned my entire workflow to automated CI pipelines.
In this github actions test automation tutorial, I’m going to show you exactly how to move from manual testing to a fully automated pipeline. Whether you’re using Jest, Pytest, or Go tests, the principles remain the same: automate the boring stuff so you can focus on building features.
Prerequisites
Before we dive into the YAML files, make sure you have the following ready:
- A GitHub account and a repository with existing code.
- A test suite that can be executed via a single command (e.g.,
npm test,pytest, orgo test ./...). - Basic familiarity with YAML syntax.
Step 1: Creating Your First Workflow
GitHub Actions works by looking for YAML files in a specific directory: .github/workflows/. If this folder doesn’t exist in your root directory, create it now.
Create a file named tests.yml. I recommend starting with a basic structure that triggers on every push and pull request to the main branch. Here is the starter template I use for most of my Node.js projects:
name: Test Automation
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
As shown in the image below, once you commit this file, you can track the progress in the ‘Actions’ tab of your repository. You’ll see a live log of the runner spinning up an Ubuntu VM, installing your dependencies, and executing your test scripts.
Step 2: Handling Environment Variables and Secrets
Most real-world tests require API keys or database credentials. You should never hardcode these in your YAML file. Instead, use GitHub Secrets.
Go to Settings > Secrets and variables > Actions and add your credentials. Then, reference them in your workflow like this:
- name: Run Integration Tests
env:
API_KEY: ${{ secrets.API_KEY }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: npm run test:integration
If you’re managing a complex set of environment variables, I highly recommend reading my deep dive on github actions secrets for testing to learn how to handle encrypted secrets across different environments (staging vs. production).
Step 3: Optimizing for Speed with Caching
The biggest complaint I hear about CI is that it’s “too slow.” In my experience, 80% of that time is spent downloading the same node_modules or pip packages over and over.
In the example in Step 1, I included cache: 'npm' in the setup-node action. This tells GitHub to store your dependencies and only re-download them if your package-lock.json changes. For other languages, you can use the actions/cache@v4 action to cache custom directories.
Step 4: Scaling Your Tests
As your project grows, a single test job can become a bottleneck. If your tests take 20 minutes to run, your development velocity drops. This is where matrix strategies and sharding come in.
A matrix allows you to run tests across multiple OS versions or language versions simultaneously. However, if you have a massive test suite, you should look into sharding tests in github actions. Sharding splits your tests into smaller chunks and runs them in parallel across multiple runners, potentially cutting your test time from 20 minutes down to 5.
Pro Tips for Robust Automation
- Use
npm ciinstead ofnpm install:ciis faster and ensures you get the exact versions locked in your lockfile. - Fail Fast: Order your tests so that the fastest, most critical unit tests run first. There’s no point running a 10-minute end-to-end test if a 2-second unit test fails.
- Artifacts for Debugging: If your tests generate screenshots or logs on failure, use
actions/upload-artifact@v4to save them so you can inspect them without rerunning the whole pipeline.
Troubleshooting Common Issues
| Issue | Likely Cause | Solution |
|---|---|---|
Permission denied |
Script not executable | Run chmod +x script.sh before committing. |
| Timeout on DB connection | Firewall/Network issues | Ensure your DB allows connections from GitHub’s IP ranges or use a service container. |
Module not found |
Incorrect working directory | Use the defaults: run: working-directory: ./folder key in your YAML. |
What’s Next?
Now that you’ve mastered the basics of this github actions test automation tutorial, you can take your pipeline further. I suggest exploring automated deployments (CD), where the code only deploys to production if the test job succeeds. You can also integrate tools like Codecov or SonarCloud to track your test coverage percentages directly in your PRs.
Ready to level up your productivity? Check out my other guides on automation tools and dev productivity tips to streamline your entire coding lifecycle.