Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Functional testing asks whether software does what its requirements say it should do. Regression testing asks whether a change, fix, or new feature has broken something that worked before. They are different testing objectives, but one test can be both: a functional check rerun after a code change is part of regression testing.

This distinction helps you choose tests, interpret failures, and decide what to run after a bug fix. It also prevents a common mistake: treating regression as a completely separate kind of test rather than a reason for rerunning existing checks.

What is the difference between functional and regression testing?

Question Functional testing Regression testing
Main objective Verify expected behavior against requirements or specifications. Detect unintended breakage after a change.
Typical trigger A feature or system behavior needs validation. A bug fix, configuration change, refactor, dependency update, or feature addition has occurred.
Test selection Cases are derived from the behavior or requirement being checked. Previously executed cases are selected for rerun; the set can be full or partial.
Timing Before or after a change, during development, or during release validation. After a change, with scope based on the change and its possible side effects.
Typical result Evidence that the intended behavior works or does not work. Evidence that existing behavior still works, or that the change introduced a regression.

“Functional” describes what the test checks. “Regression” describes why an already executed check is being run again. Selenium’s testing documentation uses the shorthand question “Are we building the product right?” for functional testing. That wording is useful because functional tests compare observed behavior with an intended specification, not merely with what the current code happens to do.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Regression testing is not limited to user-interface checks. A regression run can include unit, API, integration, database, security, performance, and functional tests. Its defining characteristic is the change-induced rerun and the search for unintended effects.

Functional testing explained

What it verifies

A functional test supplies inputs, performs an operation, and checks the resulting behavior. For a search feature, it might enter a known term and verify the expected results, empty-state message, sorting, and error handling. For an API, it might send a valid request and verify the status code, response fields, and side effects.

Where cases come from

Cases should trace to requirements, acceptance criteria, business rules, interface contracts, or a documented specification. Include normal, boundary, invalid, and permission-related paths where they are part of the promised behavior. A passing test means the observed result matches the defined expectation; it does not prove that every possible behavior is correct.

When it runs

Functional checks can run when a feature is first built, while a defect is investigated, before release, or as part of a recurring smoke suite. They do not require a recent code change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Regression testing explained

What triggers it

Run regression checks when a change could affect existing behavior. Common triggers include a new feature, defect fix, refactoring, database migration, operating-system or browser update, dependency upgrade, infrastructure change, feature-flag change, or configuration edit. The risk is not proportional only to lines changed: a small shared-library edit can affect many paths.

How the suite is selected

A full regression suite reruns all suitable existing tests. A partial or targeted suite reruns cases around the changed component and its dependencies. Good selection uses the change impact, ownership boundaries, historical failures, critical user journeys, and interfaces shared with other services. Record which tests were included and which were intentionally omitted so a narrow run is not mistaken for complete coverage.

What regression does not mean

Regression testing does not mean running only the test that failed before the fix. It also does not mean writing a brand-new test for every change and discarding established cases. New tests may be necessary, but regression value comes from checking previously working behavior for unintended damage.

Can one test be both functional and regression testing?

Yes. Suppose checkout already has a functional test that verifies a card payment succeeds. A developer changes tax calculation, and the team reruns that payment test to detect collateral damage. The test remains functional because it checks payment behavior against its specification, and it is regression testing because it was rerun after a change to find unintended breakage.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The labels answer different questions:

  • What behavior is being checked? Functional testing.
  • Why is this previously executed check being run now? Regression testing.

The same execution can also have another classification. For example, an end-to-end checkout test may be functional, automated, integration-heavy, and part of a regression run at the same time. Do not treat “functional” and “regression” as mutually exclusive phases.

Confirmation testing versus regression testing

After a defect fix, separate two purposes that are often confused:

  1. Confirmation testing (retesting): reproduce the original failure and verify that the reported defect is fixed under the same relevant conditions.
  2. Regression testing: run related and broader existing checks to discover whether the fix caused failures elsewhere.

If a login bug is fixed, confirmation testing proves that the original login scenario now succeeds. Regression testing might then cover password reset, session expiry, multi-factor authentication, account lockout, and authorization checks. Passing the confirmation test alone is not evidence that the surrounding system remains intact.

Worked example: adding a search bar

Imagine a site that previously had working navigation and account controls. A search bar is added.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Functional checks for the new feature

  • Enter a term that has matches and verify the expected results.
  • Submit an empty query and verify the specified validation or empty state.
  • Search for a term with no matches and verify the correct message.
  • Use supported filters, sorting, pagination, and keyboard submission.
  • Verify behavior for unauthorized users if search access is restricted.

Regression checks for existing behavior

  • Previously working menu buttons still open the correct destinations.
  • Header layout and navigation remain usable at supported viewport sizes.
  • Authentication, logout, and account links still work.
  • Existing API requests, caching, analytics events, and permissions are unaffected where relevant.

The search tests are functional whether run before or after release. The menu and account tests become regression checks when rerun because the search change could have altered shared header code, routing, styles, or event handlers.

Do you need to rerun existing tests after a bug fix?

Usually, yes. At minimum, rerun the original failing case as confirmation testing and a targeted regression set covering the changed code, its callers, shared components, data contracts, and critical user journeys. Run the complete suite when the change is broad, the impact is uncertain, the release is high risk, or the cost of a missed regression is high.

A practical selection process

  1. Read the defect report and identify the original preconditions, inputs, and expected result.
  2. Run the confirmation test against the fixed build.
  3. Map the changed files, services, database objects, feature flags, and external interfaces.
  4. List existing tests that exercise those areas and their dependent paths.
  5. Add critical smoke and end-to-end journeys, even if they are outside the edited file.
  6. Run the targeted set, then expand to the full suite according to risk and release policy.
  7. Investigate every failure rather than automatically labeling it a flaky test; classify product defect, test defect, environment issue, or expected change.

When a narrow run can be justified

A targeted run may be reasonable for an isolated change with clear dependency boundaries, fast feedback requirements, or a documented emergency patch. State the scope and residual risk. A narrow run is a risk decision, not proof that unaffected areas cannot fail.

Manual and automated approaches

Automation is an implementation choice, not the definition of either objective. A tester can manually perform a functional check, and an automated suite can perform regression testing. Selenium WebDriver is one example of browser automation, while Selenium Grid supports execution across multiple machines and platforms; neither is mandatory for every product.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Small browser example with Selenium

The following Python example illustrates a functional check for a search form. Adapt selectors and expected text to your application; it is not itself a complete regression suite.

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
try:
    browser.get("https://example.com/search")
    browser.find_element(By.NAME, "q").send_keys("invoices")
    browser.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
    heading = browser.find_element(By.CSS_SELECTOR, "h1").text
    assert "Search" in heading
finally:
    browser.quit()

To make this a regression check, rerun it after a relevant change and include it in the selected regression set. In a real suite, isolate test data, wait for explicit application conditions rather than arbitrary sleeps, capture screenshots and logs on failure, and run against the browser and platform combinations your support policy covers.

Building a regression strategy that scales

Layer the feedback

  • Fast checks: unit and API tests catch local contract breaks quickly.
  • Service and integration checks: verify data, queues, authentication, and external boundaries.
  • UI smoke checks: cover a few critical journeys on every build.
  • Broader UI and cross-platform runs: execute on a schedule or before high-risk releases.

Keep the suite maintainable

  • Give each test one clear purpose and stable assertions.
  • Remove duplicate coverage and quarantine genuinely flaky cases with an owner and deadline.
  • Keep test data deterministic and resettable.
  • Tag tests by component, risk, execution time, and supported platform.
  • Review the regression set when architecture, requirements, or supported browsers change.

Interpret failures carefully

A regression failure is a signal, not automatically a product bug. Check the build version, environment health, test data, browser compatibility, network dependencies, and recent selector changes. Reproduce the failure independently, preserve logs and artifacts, and document whether the behavior is an actual regression, an obsolete expectation, or a test infrastructure problem.

Performance, reliability, and cost trade-offs

Full suites provide broader confidence but consume more compute and developer time. Targeted suites provide faster feedback but can miss interactions outside the selected dependency graph. A useful policy is to run fast, high-value checks on every change, targeted regression checks for affected areas, and the broadest practical suite before releases or after high-risk changes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Parallel execution can reduce elapsed time while increasing infrastructure demand and the chance of shared-state collisions. Browser tests are especially sensitive to timing, third-party content, viewport differences, and intermittent network conditions. Make waits condition-based, control test data, record environment details, and distinguish a true product failure from an unavailable dependency.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your task is to capture web pages as test artifacts, ScreenshotNeo provides a single HTTP request instead of maintaining browser drivers. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

For the full parameter list, see the ScreenshotNeo documentation. A cURL request:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper sizes and page ranges, custom CSS and JavaScript, pre-capture clicks, hidden selectors, waits for selectors, delays or network idle, request and resource blocking, custom headers/cookies/user agents, Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed public-image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, OpenAPI, and parameter names used by other screenshot APIs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing provides two months free, and every feature is on every plan. See ScreenshotNeo or sign up free to start without a card.

Common mistakes and fixes

Calling every test “regression”

Fix: state the behavior being checked and the reason for execution. A new feature check is functional; it becomes regression when rerun after a change to detect collateral damage.

Rerunning only the failed test

Fix: perform confirmation first, then select related existing tests and critical journeys based on impact.

Assuming a passing UI test proves the whole system

Fix: combine UI checks with API, integration, data, and permission coverage appropriate to the change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Ignoring environment failures

Fix: retain logs, screenshots, browser and build versions, network information, and test-data identifiers; classify the cause before changing product code.

Using unstable waits

Fix: wait for observable application state, isolate third-party dependencies, and make retries limited and visible so they do not hide real defects.

Frequently Asked Questions

Is smoke testing the same as regression testing?

No. Smoke testing is a small set of checks for basic build stability. Those checks can be included in a regression run after a change, but smoke describes scope and purpose differently from regression.

Does regression testing require automation?

No. Regression can be manual or automated. Automation is useful for repeatability and speed, while the regression label comes from rerunning existing checks after a change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should a new test be added after every defect?

Add a test when it provides durable coverage of the defect and its expected behavior. Also run relevant existing tests; a new confirmation case alone is not broad regression coverage.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.