Test automation · End-to-end QA
Carpooling Test Automation
A test strategy and end-to-end suite over a carpooling web application, from requirements to anomaly tracking.
A full QA activity over a carpooling web application: requirements analysis into a test strategy, test plans and data sets, an end-to-end Selenium framework built on page objects and explicit synchronisation, a Postman layer over the REST API, regression runs in GitLab CI, and anomaly tracking in Jira.
Problem
The value of a carpooling app lives in journeys that cross several features at once: a passenger joins a trip, the driver confirms, the trip completes, and only then does a debt appear between two people. Unit tests on either side of the API cannot reach that. End-to-end tests can, but they are also the first suite a team deletes, because they break on every markup change and fail intermittently on asynchronous updates.
Objectives
- Derive the test strategy from the requirements and functional specifications, so coverage is decided by what the product promised rather than by what the code happens to expose.
- Prove the journeys that matter end to end, rather than the units on either side of the API.
- Cover the areas where a regression is visible to a user: authentication, vehicles, trips, friends and debts.
- Make the suite maintainable enough to survive UI change instead of being deleted after the first redesign.
- Make it reliable enough to be trusted in CI, because a suite that fails intermittently stops being read.
Implementation
- Started from the requirements and functional analysis: a test strategy naming what must be covered and to what depth, then test plans, test cases and the data sets each case runs with — written before the automation, so the automation has a specification to satisfy.
- Page Object Model: each screen owns its selectors and exposes methods in the language of user intent, so a test reads as a journey and a markup change is fixed in one file.
- Reusable components for the elements that recur across screens, so shared UI is modelled once instead of re-described in every page object.
- Explicit waits bound to the specific condition each step depends on. A single-page app updates asynchronously; fixed sleeps are simultaneously too long for a fast machine and too short for a slow one, which costs both time and trust.
- Data-driven cases: one journey definition exercised across multiple inputs, separating what is being tested from what it is tested with, so adding a case is adding data rather than code.
- Assertions made on what the interface presents after an action rather than on internal state, because that is the thing a user would notice breaking.
- A Postman collection covers the REST API as a separate layer, so a broken contract is caught without going through the interface and a UI failure can be attributed to the frontend or the backend rather than guessed at.
- Functional and regression runs execute in GitLab CI, and results, anomalies and test campaigns are recorded in Jira so a defect has a trail from the failing case to its fix.
Architecture
Test strategy
- requirements analysis
- functional specifications
- test plans
- test cases
- data sets
Test framework
- Selenium WebDriver
- browser session
- Page Object Model
- reusable components
- explicit waits
- UI-level assertions
- pytest fixtures
- data-driven cases
Suites
- authentication
- vehicles
- trips
- friends
- debts
- key journeys
- Postman REST collections
Continuous integration
- GitLab CI
- functional + regression runs
- Jira: anomalies & test campaigns
Technologies
- Selenium
- WebDriver
- Python
- Pytest
- Page Object Model
- Postman
- GitLab CI
- Jira
Challenges & solutions
Challenge
End-to-end suites are usually abandoned, not because they find nothing but because every UI change breaks dozens of tests at once.
Solution
Confine selector knowledge to page objects. The cost of a redesign then scales with the number of screens changed, not with the number of tests that touched them.
Challenge
Asynchronous UI updates make naive tests fail intermittently, and an intermittently failing suite is quickly ignored, which is worse than having no suite at all.
Solution
Wait on conditions, never on the clock. Each step blocks until the state it needs is actually present, which is both faster than a conservative sleep and more reliable than an optimistic one.
Challenge
The debt logic is where a bug costs the most, since it concerns money between people, and it only becomes observable after a multi-step journey.
Solution
Exercise it as a journey across screens rather than as an isolated check, so the assertion lands on the state a user would actually see and dispute.
Challenge
A suite that repeats the same flow for every input becomes unmaintainable long before it becomes complete.
Solution
Drive cases from data, so coverage grows by adding rows rather than by copying test functions.
Challenge
A failing end-to-end test proves something is broken but not where, and chasing a UI symptom into the backend is the slowest way to find out.
Solution
Test the REST API separately with Postman. If the API layer passes and the UI fails, the fault is in the frontend; if both fail, it is not. The second layer pays for itself in diagnosis time.
Results
- The test cases trace back to the requirements rather than to the implementation, so the suite says what the application was supposed to do instead of restating what it currently does.
- Two layers rather than one: Postman exercises the REST API directly, which localises a contract break to the backend in seconds, while the Selenium suite proves the journey a user actually takes.
- Six functional areas are exercised through the real interface, including the money paths where a regression is most expensive.
- Selector knowledge lives in the page classes, so a renamed element is a one-line change rather than a sweep across the suite.
- Waiting on conditions rather than on the clock removes the intermittent failures that make a suite get ignored, which is what decides whether it is allowed to gate a merge.
Lessons learned
- Writing the test plan before the automation changes what gets automated. Starting from the requirements surfaced cases that would never have occurred to me reading the code, because the code cannot tell you what it was supposed to do.
- A test suite's real enemy is maintenance cost, not missing coverage. The indirection of the Page Object Model is worth paying for precisely because it decides whether the suite survives the next redesign.
- Explicit waits are not a style preference. Sleeps trade speed against reliability and lose on both, and reliability is what earns a suite the right to block a merge.
- End-to-end tests justify their cost on the paths that cross several features at once — which is exactly where unit tests on either side of the API cannot see.