How to Test PrestaShop Modules Properly
A practical testing workflow for PrestaShop modules: pre-flight checks, functional testing, regression testing, and a printable checklist.
Why we test every module across supported PrestaShop versions before shipping
We ship 140+ modules out of mypresta.rocks, and every one of them runs through PS 1.7.6, 1.7.8, 8.x and 9.x on dedicated dev containers before the release ZIP leaves our server. We didn't start there. We started by trusting that "tested on PS 1.7" meant something. Then we shipped a module that worked on 1.7.8 and white-screened on 1.7.6 because StockAvailable::getQuantityAvailableByProduct changed signature mid-minor. That was the day we built the multi-container test rig.
This page is the workflow we actually use — for our own releases and for clients testing third-party modules on their shops. It's biased toward the tests that catch real bugs, not the tests that look thorough on paper.
The cheapest bug is the one your dev container catches at 3pm. The most expensive is the one a customer reports on a Friday night live shop. Every step below exists because we paid for it the hard way.
Before you install: the three checks that matter
Most "this module broke my shop" tickets we see could have been prevented by reading three things first. None of them take more than five minutes.
Exact version compatibility, not major version
"Compatible with PrestaShop 1.7" tells you almost nothing. PS 1.7.0 and 1.7.8 are five years and several breaking core changes apart. Same for 8.0 vs 8.2. Open the module's config.xml or composer.json and look at ps_versions_compliancy:
<ps_versions_compliancy>
<min>1.7.6.0</min>
<max>9.0.99</max>
</ps_versions_compliancy>
If your shop is below the min or above the max, install will fail outright — or worse, it'll install and crash later on a hook the module assumed existed.
PHP version is the same story. A module written for PHP 8.1+ using typed properties and readonly classes will fatal on PHP 7.4 with a parse error before PrestaShop even loads. Check your shop's PHP version in Advanced Parameters → Information and match it to the module's requirements.
Override count
Unzip the module and look at the override/ directory. If you see override/classes/Cart.php, override/controllers/front/OrderController.php, anything in there at all — that's the single biggest predictor of conflicts with whatever else you already have installed. Two modules can't override the same method. The second one to install just fails.
Hook-based modules are nearly always safer than override-based ones. If the module description says "uses overrides" and you already have a checkout module, an SEO module, or anything that touches Cart or Product, expect trouble.
What it actually hooks
Open the module's main PHP file and search for registerHook. The list tells you exactly where this module is going to inject itself. A "simple banner module" that registers on actionFrontControllerSetMedia, displayHeader, actionCartSave, and actionValidateOrder is not a simple banner module — it's doing things you should know about before installing.
The staging environment must match production
A staging site that's "close enough" to production isn't staging — it's a different shop you happen to also own. The whole point is reproducing the exact conditions the module will hit on the live site. If your staging runs PHP 8.1 and production runs 7.4, your tests prove nothing.
Our rule for client staging: same PHP, same PrestaShop, same theme, same modules, same database (refreshed within the last seven days), same web server. We use Docker containers per shop precisely because they make this trivial — `docker compose` brings up an identical environment in under a minute. See our staging site guide for the setup we use.
The testing workflow
Phase 1: backup, even on staging
Yes, even on staging. You're about to spend 30 minutes testing — losing the database to a botched install means re-cloning production, which is the slow part. Dump first:
# Native
mysqldump -u root -p prestashop > ~/backup_before_module.sql
# Docker (matches how we run it)
sudo docker exec <your-shop>-db mysqldump -u root -p'password' prestashop > ~/backup_before_module.sql
And snapshot the modules/ directory while you're at it. If the module's installer drops files into other modules' directories (yes, we've seen this) the file snapshot is what saves you.
Phase 2: install and watch the logs
Don't just upload and click around. Tail var/logs/ while the installer runs:
tail -f var/logs/*.log
If the installer fails, you want the actual error, not "module installation failed" in red. Common failures we see: missing PHP extensions (intl, gd, curl), insufficient file permissions on modules/, duplicate role slugs in ps_authorization_role from a previous failed install. The last one is why our internal nuke-module.sh script exists — it deep-cleans every trace of a module so the next install starts from zero.
Phase 3: functional testing — what the module claims
Go through the module's feature list one item at a time. Configuration page loads, settings persist after saving, every documented feature actually does what it says. Check the back-office menu position the docs promised. Switch the back-office language and verify translations exist for whatever your team uses.
Then the front office: visit every page type the module is supposed to affect. Open DevTools, watch the console for JavaScript errors, watch the Network tab for failed requests and 404s on the module's own assets. A module that adds views/css/style.css via raw <link> tags instead of registerStylesheet won't get cache-busted — that's a 404 waiting to happen the next time you clear the cache.
Phase 4: regression testing — what else broke
This is the step everyone skips and it catches more bugs than every other phase combined. The module works. Now: does everything else still work?
Our checkout-revolution and performance-revolution modules touch enough of the cart and rendering pipeline that we have a fixed regression script for them — six paths we run every release:
- Add to cart from a product page, from a category listing, and from search results. Three places because they hit three different controllers.
- Complete a checkout end-to-end with a real payment method and verify the order appears in admin with the correct totals.
- Register a new customer and confirm the welcome email fires.
- Search for a product by name, by reference, and by EAN.
- Admin order detail opens cleanly — no PHP notices, no broken tabs, no missing carrier info.
- Empty cart, log out, log in as the same customer and confirm history is intact.
If checkout works end-to-end, you're most of the way there. Most module conflicts surface during checkout because it's where the most hooks fire, the most JavaScript runs, and the most AJAX endpoints get hit on a single page.
Phase 5: performance — open the Network tab
Some modules add real overhead. We've audited shops where a single "social proof" module added 1.4 seconds of TTFB because it made a synchronous external API call inside hookDisplayHeader. The kind of thing that wrecks Core Web Vitals and shows up in Search Console six weeks later.
Three quick checks before you accept the module:
- DevTools Network tab — what new requests is the module adding, and how big are they? A 300KB JS bundle on every page is not free.
- Server-side timing — install with debug mode briefly, look at the Symfony toolbar's "Performance" tab. Module hooks that take more than 50ms each are worth a closer look.
- External calls inside hooks — grep the module for
curl_exec,file_get_contents('http,fopen('http. Synchronous outbound calls from any hook that fires on a page render is an instant red flag.
Testing module updates
Updates are usually riskier than fresh installs. Fresh installs you control the timing of. Updates can land via auto-update or via Addons Marketplace one-click installs and surprise you.
Read the changelog before clicking update. If it mentions database schema changes, run the upgrade on staging first and check upgrade/install-X.Y.Z.php actually executes — incomplete upgrades are the #1 source of "module is broken after update" tickets we field. After updating, clear the Smarty cache and the class index together:
rm -rf var/cache/prod/*
rm -f var/cache/prod/class_index.php
Then run the full Phase 4 regression. The "I just updated, what could break" instinct is wrong — settings can be lost, default configs can be overwritten, and overrides can silently stop working when the parent method changes signature.
Multi-version testing — how we actually do it
For developers shipping modules across PS versions, the only honest answer is dedicated containers per version. We run four version tracks concurrently:
| Container | PS | PHP | What it catches |
|---|---|---|---|
ps176-dev | 1.7.6 | 7.4 | Legacy shops — surprisingly many still run 1.7.6 |
ps178-dev | 1.7.8 | 7.4 | The most common version in production today |
ps8-dev | 8.x | 8.1+ | Hook deprecations, Symfony controller migration, new tax engine |
ps9-dev | 9.x | 8.3+ | AdminAPI, removed APIs, Symfony service decorators, Hummingbird |
Our release workflow runs the module through all four before the ZIP is signed off. The container choice matters: a "PS 9 compatible" claim from a vendor that only tested on 9.0 means almost nothing — 9.1 added breaking changes to the catalogue tree and removed several legacy hooks. We use our certify command to scan the built ZIP for deprecated API calls before it's allowed into releases/.
When something goes wrong on staging
Back office is inaccessible after install
Don't panic, and don't reinstall PrestaShop. SSH in, rename the module directory out of the way, and clear the cache:
mv modules/badmodule modules/badmodule_disabled
rm -rf var/cache/prod/* var/cache/dev/*
Back office should come back. Now grab the error from var/logs/ and send it to the vendor — that's the data they need to fix it. A vendor that can't reproduce from a log line is one that doesn't have a dev shop matching your version. Treat that as data about whether to keep using them.
White screen on the front office
Edit config/defines.inc.php and flip _PS_MODE_DEV_ to true. Reload. You'll get the actual stack trace. Read the file path in the trace — if it's inside the new module, that's your culprit; if it's in core but the parameters look wrong, you're probably looking at a hook return-value bug (action hook code accidentally registered on a display hook, or vice versa).
Flip dev mode back to false the second you have what you need. Live shops with dev mode on expose stack traces and on PS 9 will leak DB credentials in error pages.
Two modules conflict
The bisection is dull but reliable. Disable both. Enable A (full Phase 4 regression. Enable B) Phase 4 again. If it only breaks with both active, you're looking at one of three things: same display hook position fighting, same core class overridden by both, or competing JS libraries (different jQuery versions or two CSS frameworks both claiming .container).
Override conflicts log a line you can grep for:
grep "is already overridden" var/logs/*.log
The fix is manual merging. Open both override files, combine the logic, install the second module, drop var/cache/prod/class_index.php. Document what you merged — next time either module updates, you'll need to re-merge.
Automated testing for module developers
If you're writing modules (not just installing them) the test investment pays back fast. Here's what we actually run.
PHPUnit for any class that has logic separable from PrestaShop. Tokenizers, calculators, formatters, schedulers. Mock the PrestaShop calls at the boundary; test the business logic without spinning up a shop. We run these on every commit via our ship workflow before the ZIP is even built.
Playwright for front-office smoke tests. Add-to-cart, checkout, login. The kind of test that catches "your new release broke the buy button" before a customer does. We keep these to under 30 seconds total runtime — anything slower stops getting run.
GitHub Actions wiring it together. Push a commit, the workflow spins up PS 1.7.8 and PS 9.1 in containers, installs the module, runs PHPUnit, runs the Playwright suite, then runs our certify command on the built ZIP to flag deprecated PrestaShop API calls. If anything red lights up, the release ZIP doesn't generate.
The checklist we actually use
Pinned to the wall above our dev rack. Twelve items, in order. We don't ship a release until every line is green.
| # | Check |
|---|---|
| 1 | Staging is a fresh clone of production (database refreshed within 7 days) |
| 2 | Database + modules/ snapshot taken before install |
| 3 | Module installs cleanly — no entries in var/logs/ |
| 4 | Configuration page loads, settings save and persist after a reload |
| 5 | Every documented feature works as advertised |
| 6 | DevTools console clean on every affected page |
| 7 | Add to cart works from product page, category, and search |
| 8 | End-to-end checkout completes with the order showing correctly in admin |
| 9 | Customer registration and login still work |
| 10 | Search returns correct results for name, reference, and EAN |
| 11 | Mobile viewport — no layout breaks on a 375px-wide screen |
| 12 | Page-load delta under 200ms compared to baseline |
Every module from mypresta.rocks ships with a free 30-day demo so you can run this checklist on your own staging before paying for anything. Run it. If something fails, tell us — that's exactly the feedback that makes the next release better.
Related reading
- How to Create a PrestaShop Staging Site — the testing environment this whole workflow depends on
- Essential Tools for PrestaShop Development — what we use day-to-day to inspect modules
- PrestaShop Local Development: XAMPP, WAMP, Docker & Linux Setup — local container setup matching ours
- PrestaShop Hooks and Overrides — understanding the hook system most module bugs trace back to
Related modules
- Performance Revolution — catch performance regressions before release
- MPR Checkout Revolution — regression-test the full checkout path
- Issue Tracker — log reproducible bug reports from testing