Regression testing is the process of rechecking an application after changes to detect defects in previously working functionality. Every code change — a new feature, a bug fix, or refactoring — can unintentionally break existing application capabilities. Regression tests automate the verification that old functionality remains operational. According to a study by IBM, 2023, regression testing covers 30 to 70% of all executed tests in commercial product teams, highlighting its role as the primary barrier against production incidents.
Key Takeaways
Regression testing is a type of testing aimed at confirming that code changes have not broken existing functionality. The term “regression” means a return to a worse state — when a function that worked in the previous version stops working in the new one. Regression tests are executed repeatedly on each development cycle, which distinguishes them from new feature tests that are written once.
The need for regression testing stems from the cascading changes effect: fixing a bug in one module may resolve the issue but break adjacent functionality that depended on it. For example, changing an SQL query in the user repository may speed up authentication but break data export that used the same query. A regression test on data export will catch this violation before release.
According to the CISQ 2023 report, the cost of fixing a regression defect found in production is 15 times higher than at the automated regression run stage. Companies investing in automated regression testing reduce the share of regression defects in releases from 25% to 5% within a year of implementation, according to the Capgemini World Quality Report.
There are several approaches to regression testing that differ in scope and test selection criteria. The choice of approach depends on the project size, frequency of changes, and available time in the CI pipeline. Below are the main types of regression testing with their characteristics.
Full regression run executes all automated tests of the project without exception. This approach provides maximum confidence but requires significant computing resources and time. A full run is performed before major releases — every 2–4 weeks. For an application with 5000 tests, a full run takes 2 to 6 hours depending on infrastructure.
Selective approach runs only tests related to the changed modules. To determine relatedness, code-level dependency analysis is used: if the UserRepository class is changed, tests depending on UserRepository directly or transitively are run. Tools like Jacoco, Android Test Coverage, and Xcode Code Coverage provide coverage maps for accurate selection. A selective run is performed on each pull request and takes 5–15 minutes.
Risk-based regression ranks tests by functionality criticality and likelihood of breakage. Critical functions — payments, authentication, synchronization — are tested on every code change. Auxiliary functions — the About screen, animations — are tested only before release. Ranking is reviewed quarterly based on production incident data.
The concepts of regression testing and retesting are often confused, although they are different processes. Retesting is a re-run of a specific test that previously failed, after a defect fix. The purpose of retesting is to confirm that the fix works: the bug no longer reproduces. Retesting is performed once, immediately after the fix and the developer’s confirmation of the fix.
Regression testing is running tests on existing functionality that has NOT been changed. The goal is to ensure that fixing one defect has not created a new defect elsewhere. Regression tests are run repeatedly on each development cycle, regardless of which specific bugs were fixed. The main difference: retesting verifies the fix itself, regression verifies the consequences of the fix.
In a CI/CD pipeline, both processes run sequentially. After merging a pull request, a retest of the specific bug is run, followed by a full or selective regression run. According to SmartBear (2022), separating these processes reduces CI run failure diagnosis time by 30%, since the team immediately sees which defects are related to regression and which to non-working fixes.
Regression test automation is a critical success factor for modern mobile projects. Manual regression testing does not scale: with a suite of 200 tests, one run requires 2–3 working days of a QA engineer, making daily runs impossible. Automated regression tests run in 10–60 minutes without human intervention, allowing them to be executed on every commit or pull request.
To keep the regression suite up to date, test analytics is used: tools like Allure, ReportPortal, and Xray track pass rates, duration, and stability of each test. Tests whose stability drops below 90% (often breaking due to requirement changes) are marked as legacy and assigned to the owner for review.
Let us look at setting up an automated regression test on Android using the JUnit 5 library and Espresso. The example demonstrates selective regression — the test verifies that after refactoring the user repository, the profile screen is not broken. For iOS, XCTest with similar logic is used — a repeated test on a key scenario.
The test uses MockWebServer to emulate the server and checks the full path: loading user data, displaying it on the profile screen, and handling an error when the server is unavailable. Such tests are included in the regression suite and run on every change in the module-profile.
@RunWith(AndroidJUnit4::class)
class ProfileRegressionTest {
@get:Rule
val composeRule = createComposeRule()
@Test
fun profileScreen_rendersCorrectly() {
val user = User(id = 1, name = "Alice", email = "alice@test.com")
composeRule.setContent {
ProfileScreen(user)
}
composeRule.onNodeWithText("Alice").assertIsDisplayed()
composeRule.onNodeWithText("alice@test.com").assertIsDisplayed()
}
@Test
fun profileScreen_handlesNetworkError() {
setNetworkError()
composeRule.onNodeWithText("Loading error").assertIsDisplayed()
}
}
For iOS, the regression test uses XCTestExpectation for asynchronous verification of UI updates after receiving data from the API. The test emulates a network response and verifies that the UI elements updated correctly.
class ProfileRegressionTests: XCTestCase {
func testProfileScreen_rendersCorrectly() {
let viewModel = ProfileViewModel(userId: 1)
let view = ProfileView(viewModel: viewModel)
viewModel.loadProfile()
let expectation = expectation(description: "profile loaded")
viewModel.onProfileLoaded = {
XCTAssertEqual(viewModel.userName, "Alice")
XCTAssertEqual(viewModel.userEmail, "alice@test.com")
expectation.fulfill()
}
waitForExpectations(timeout: 3.0)
}
}
Building an effective regression suite is an iterative process based on defect and code change data. The initial strategy is to include all existing tests in the regression suite and run a full pass before each release. As the test base grows (over 2000 tests), a full run becomes too long and a selective approach is required.
The second phase — implementing dependency analysis tools: Jacoco for Android, Xcode Test Plan for iOS. These tools build a “test — class — method” map and allow determining which tests are affected by a specific change. A selective run based on coverage analysis reduces execution time by 60–80% while maintaining 95% regression detection effectiveness, according to Spotify Engineering (2022).
The third phase — continuous monitoring and optimization. Tests that have not failed in 6 months are moved to a low-priority suite. Tests that fail more than once a month are candidates for review: either they catch real problems (need a fix) or they are too brittle (require stabilization). A quarterly review of the regression suite is standard practice to maintain its effectiveness and execution speed.
Frequently Asked Questions
Selective regression run — on every pull request. Full regression run — before each release and weekly (nightly build). The key rule: the more frequent the run, the faster regressions are detected and the lower the cost of fixing them. For critical projects, full regression on every merge is possible.
All unit tests (basic regression), integration tests on key components, and UI tests on critical user scenarios. Do not include tests for experimental functionality, tests with flakiness above 10%, and tests requiring a manual environment.
Remove tests for removed functionality, update tests when requirements change, conduct a quarterly audit of the suite. CI analytics — Allure, ReportPortal — helps identify tests that have lost relevance: if a test has not changed or failed for 3 months, it is a candidate for removal from the daily run.
Use parallel test execution on multiple devices, implement selective regression based on changed code coverage analysis, disable visual snapshots for irrelevant screens. Target time for a selective run is 5–10 minutes, for a full run — no more than 2 hours.
No, regression testing also includes manual checks: exploratory testing after release, UX regression, and accessibility checking after interface changes. Automation covers 70–80% of regression checks; the remaining 20–30% are manual, focusing on scenarios that are impossible or too expensive to automate.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also