Evaluating CodeRabbit? Same review, plus real test runs. See why

Automation Testing13 min readUpdated September 19, 2026

Functional Testing: Types, Process, Test Case, and Tools

S
Technical Writer, Qodex
The filled test case CHK-001 from the guide: requirement REQ-114, expected total 45.00 plus tax, actual order 100238 with stock 4, status pass on build 2026.9.14-rc2

Functional testing checks whether software does what its requirements say it should do. A tester supplies an input or performs a user action, then compares the actual result with the expected result. It covers individual units, integrations, complete workflows, APIs and user acceptance. Teams can run it manually or automate repeatable checks. It answers "Does this feature work?", not "How fast or secure is it?"

Part of our Software Testing guide. Read the guide

If you would rather not write and rerun these checks by hand, Qodex runs API and UI scenarios against your app on every pull request and deploy. See Qodex UI testing.

What is functional testing?

Every functional test starts with a requirement. That requirement might be a written specification, a user story, an acceptance criterion, or a business rule the team already agrees on. The test feeds the system an input or an action, watches what comes back, and compares it with what the requirement promised. IBM puts it plainly: functional testing "verifies whether an application's features work as expected based on the specified requirements". Source: IBM, read 18 September 2026.

In QA, functional testing verifies behavior against requirements. That is the whole job. It does not ask how the feature is built, how quickly it responds, or how it looks. It asks whether the output matches the promise, including the promises about what should happen when something goes wrong.

Functional testing is often black-box work. The tester drives the application through its interface and never reads the code. It is not a synonym for black-box testing, though. A unit test that checks a pricing function from inside the codebase is white-box, and it is still a functional test, because it compares behavior against a requirement. Sources: IBM and Applause, read 18 September 2026.

Why functional testing matters

Broken business rules are expensive and quiet. A coupon that takes its discount off the wrong subtotal, a tax rule applied to an exempt item, a password reset link that never arrives: none of these crash anything. They produce wrong answers, and they keep producing them until a customer complains or an accountant notices.

Functional tests catch that class of fault because they hold the software to a stated expectation instead of watching for crashes. They also cover error handling, which is where most requirements are thinnest. A form that accepts a negative quantity or swallows a declined card without telling the user is a functional defect, even when every page loads.

The second payoff is faster diagnosis. A failing functional test names the requirement it was checking, so the team starts from the business rule rather than from a stack trace. The paths worth this attention are the ones carrying money, data and access: sign-up, sign-in, search, checkout, permissions and integrations.

Types of functional testing

Types of Functional Testing

The types below are sorted by scope, from a single function to a signed-off release. Most teams run several of them at the same time.

TypeScopeWhat it provesExampleUsually run by
Unit testingOne function or class, in isolationThe smallest piece of logic returns the right outputA tax function returns zero for an exempt itemDevelopers
Component testingOne module with its dependencies stubbedA module behaves correctly on its ownThe cart module totals line items correctlyDevelopers
Integration testingTwo or more modules working togetherData crosses a boundary intactThe cart hands the order to the payment serviceDevelopers or QA
API testingRequests and responses at the service layerEndpoints return the right status, body and errorsA create-order request returns 201 and an order idQA or developers
System or end-to-end testingThe whole assembled applicationA complete user workflow finishesSign in, add an item, pay, get a confirmationQA
Smoke testingA thin slice of the build's core pathsThe build is worth testing furtherThe app loads and a user can sign inQA or the CI pipeline
Sanity testingThe area touched by a recent changeA narrow fix did what it claimedThe coupon field accepts a fixed code after a patchQA
Regression testingBehavior that already worked, after a changeNothing that passed before is brokenLast quarter's checkout suite after a payment upgradeCI, usually automated
Acceptance testingBusiness rules and user goalsThe software meets the agreed requirementFinance confirms invoices carry the right taxProduct owners and end users

These labels overlap, and arguing about the boundaries wastes time. Smoke, sanity and regression describe when and why you run a set of tests, not what those tests check; the tests inside them can include functional tests. Unit, integration, system and acceptance describe how much of the system is assembled when the test runs.

Pick the smallest scope that can prove the rule. A discount calculation belongs in a unit test, where it runs quickly and points at the faulty line. The same rule checked only through the browser is slower, breaks when a button moves, and says little about the cause. Save end-to-end tests for the few workflows that cross every layer, and cover the interface itself with a short set of high-value journeys.

How to perform functional testing

1. Read the requirements and find the gaps

Collect the specification, the stories and the acceptance criteria, then write down every rule that can be checked. Ambiguity shows up fast at this stage. "The coupon applies a discount" is not testable; "a valid coupon reduces the item subtotal by 10 percent before tax and shipping" is. Take the unclear ones back to the person who wrote them before you write a single case.

2. Plan and rank by risk

List the features in scope and order them by what a failure would cost. Payment, authentication, permissions and anything that writes to a customer record sit at the top. Decide what is out of scope and say so, so nobody assumes coverage that does not exist.

3. Set up the environment and the test data

Functional tests need an environment close enough to production that the results mean something, and data you control. Create the accounts, catalog items, coupons and payment fixtures in advance, and make them repeatable. Test data is a common source of flakiness: a test that passes only on a fresh database is not a test, it is a coincidence.

4. Write the test cases

Each case gets an id, the requirement it traces to, preconditions, data, steps, and one expected result. Cover the happy path, the negative paths and the boundaries. For a full treatment of case structure, see writing test cases in software testing.

5. Execute

Run the cases in a known order against a known build. Record the build number with the result. A pass against an unknown build proves nothing later, when someone asks whether the fix was in.

6. Compare, log and be specific

Compare actual with expected, field by field, and log what differs. A useful defect report carries the case id, the build, the data used, the steps, the expected result and the actual result. Vague reports get bounced back, and the round trip is wasted time.

7. Retest, close and maintain

Retest the fix with the original case, then run the cases around it in case the fix broke a neighbor. Close the defect only when the case passes. Then prune: cases that no longer trace to a live requirement should be deleted rather than left failing, because a suite nobody trusts gets ignored.

A worked functional test case

Most guides show an example workflow and stop there. Here is a filled case you can copy into your own tracker. The application is an online store; the requirement is that a shopper can buy an in-stock item with a valid card. This is an editorial example, not a report from a live store.

FieldValue
IDCHK-001
RequirementREQ-114: a signed-in shopper can complete checkout for an in-stock item using a valid card, and the order is recorded once
PreconditionsTest account shopper@example.test exists and is signed in. Cart is empty. SKU BLU-42 shows 5 units in stock. Payment sandbox is reachable.
Test dataSKU BLU-42, quantity 1, price 40.00 USD. Shipping: 12 Test Lane, Springfield, 62704, standard shipping 5.00 USD. Card 4111 1111 1111 1111, expiry 12/2030, CVV 123.
Steps1. Open the product page for BLU-42. 2. Add one unit to the cart. 3. Open the cart and confirm the subtotal is 40.00 USD. 4. Proceed to checkout. 5. Enter the shipping address and choose standard shipping. 6. Enter the card details. 7. Submit the order.
Expected resultOrder total is 45.00 USD plus tax. A confirmation page shows an order number. Exactly one order exists for the account with status Paid. Stock for BLU-42 drops from 5 to 4. A confirmation email is queued to shopper@example.test.
Actual resultOrder 100238 created, total 45.00 USD plus 3.30 USD tax, stock 4, one confirmation email queued.
StatusPass, build 2026.9.14-rc2, run 18 September 2026

The positive case is the smaller half of the work. Refusals are where requirements tend to be thinnest, so each negative variant below reuses CHK-001 and changes one thing.

VariantChange from CHK-001Expected result
CHK-002, expired cardCard expiry 01/2020Payment is declined with a message naming the expiry. No order is created. Stock stays at 5. The cart keeps its contents.
CHK-003, invalid couponCoupon code EXPIRED10 entered at checkoutThe coupon is rejected with a clear reason. The total stays 45.00 USD plus tax. Checkout can still be completed without the coupon.
CHK-004, out-of-stock itemSKU BLU-42 set to 0 units before step 7The order is blocked before payment. No charge is made. The shopper is told the item is unavailable and the cart shows the item as out of stock.

Two details make these cases worth keeping. Each one names a single expected outcome per check, so a failure points at one rule. Each one also asserts the state after the action, not just the message on screen: stock level, order count and the queued email are what tell you the write path worked. A test that only reads the confirmation page will pass while the inventory silently stays at 5.

Functional testing techniques

Techniques are ways to choose inputs so a small number of cases covers a large input space.

  • Equivalence partitioning. Group inputs that should behave the same way and test one value from each group. An age field that accepts 18 to 65 has three groups: below 18, 18 to 65, above 65. Three cases, not fifty.

  • Boundary value analysis. Test the edges of those groups, where off-by-one mistakes live: 17, 18, 65 and 66. Pair it with equivalence partitioning rather than using it alone.

  • Decision tables. When an outcome depends on several conditions at once, list the combinations in a table and test each row. Free shipping that depends on order value, membership tier and destination has eight combinations, and a table stops you from guessing which ones matter.

  • State transition testing. Use it where an object moves through states: an order that goes from created to paid to shipped to refunded. Test the legal moves, and test the illegal ones too, such as refunding an order that was never paid.

  • Use case testing. Walk a full user goal end to end, including the alternate paths people actually take, such as editing the address after the payment step.

  • Error guessing. Unstructured, experience-led testing: empty fields, pasted whitespace, a double-clicked submit button, the back button mid-checkout. It finds real defects, but it is a supplement, not a plan, because nobody can repeat it reliably.

Functional vs non-functional vs regression testing

QuestionFunctionalNon-functionalRegression
What does it ask?Does the feature work as the requirement says?How well does the system perform, scale and hold up?Does everything that worked before still work?
What triggers it?A new or changed featureA release carrying performance, load or security riskAny change: a feature, a fix or a dependency upgrade
ScopeThe behavior under testQualities of the whole systemExisting behavior across the product
ExampleA valid coupon reduces the subtotal by the amount the rule specifiesCheckout still answers inside its agreed response time at peak loadThe checkout suite rerun after a payment library upgrade

Functional and non-functional testing answer different questions about the same build, and you need both before a release. Keep them apart in reporting, or a slow response gets filed as a broken feature. For a working list of what belongs in each, see the functional and non-functional testing checklist. Regression is not a third kind of check at all: it describes why tests are rerun, and the suite that runs can include functional tests you already wrote. The full comparison is in functional testing vs regression testing. Sources: CircleCI and IBM, read 18 September 2026.

Manual or automated functional testing?

Most teams use both, and the split is decided per case rather than per project. Automate what is stable, repeatable and run often: the checkout suite, the API contract checks, the permission matrix, anything that has to pass before every release. Those cases pay back because the cost is the writing, and the running is close to free.

Keep manual testing for the work a script cannot judge. Exploratory sessions on a new feature, a first pass on a design that is still moving, usability questions, and one-off checks on a flow that will change next sprint. Automating a screen that is being redesigned is how suites rot.

Two traps are worth naming. Automating everything produces a suite so slow and brittle that people skip it, which is worse than having no suite, because the red build stops meaning anything. Automating too early, before the interface settles, produces the same result by a different route. Start with the handful of flows that would hurt most if they broke, get them green and fast, and grow from there.

Functional testing tools by job

Pick by the job in front of you, not by a ranking. The table below covers the seven jobs most teams have to fill.

JobGood starting optionsBest fitMain tradeoff
Unit and component testsJUnit for Java, pytest for Python, Jest for JavaScriptThe team that owns the codeFast and cheap, but proves nothing about assembled flows
Cross-browser web UIPlaywright, Cypress, SeleniumJourneys a user clicks throughSlowest to run and the most sensitive to UI change
API and service checksPostman, REST Assured, an HTTP client inside your test frameworkBusiness rules behind the interfaceMisses anything that only breaks in the browser
Mobile appsAppium, or the platform's own test framework such as Espresso on AndroidNative and hybrid apps on devicesDevice and emulator upkeep is ongoing work
Windows desktop GUITestComplete, WinAppDriverLegacy desktop applicationsWindows only, and licensing is a purchase decision
Visual checksApplitools, Playwright visual comparisonsLayout faults that assertions missNoisy failures whenever the design changes
Continuous pull-request executionQodexTeams that want the suite run on every changeRuns against a deployed app, so it needs an environment to hit

Each starting option above describes itself the same way its row does. JUnit calls itself "the programmer-friendly testing framework for Java and the JVM", Jest "a delightful JavaScript Testing Framework with a focus on simplicity", and REST Assured a way of "testing and validating REST services in Java". Espresso is Android's own instrumentation framework, WinAppDriver is Microsoft's Windows Application Driver, and Playwright documents screenshot comparison under "Visual comparisons". Sources: JUnit, Jest, REST Assured, Espresso, WinAppDriver and Playwright visual comparisons, read 19 September 2026.

Two vendor facts are worth having before you shortlist. Postman lists Free at 0 USD, Solo at 9 USD per month and Team at 19 USD per user per month, both billed annually. The free plan includes 1,000 monitoring requests per team per month. Source: Postman pricing, read 18 September 2026. TestComplete publishes no price: it advertises a 14-day trial, requires Windows, and asks you to contact the vendor for a quote. Source: SmartBear, read 18 September 2026.

People also compare testRigor and Katalon against these. This guide does not rank them, because no current public source was clean enough to quote for their plans or limits. For a longer list with the tradeoffs spelled out, see top functional testing tools for automation.

Best practices and common mistakes

  • Trace every case to a requirement. A case that cannot name the rule it checks is either testing an implementation detail or testing nothing.

  • Assert state, not just screens. Check the database row, the API response and the queued message, not only the confirmation banner.

  • Write negative cases first for anything involving money or access. Refusals are where the requirements are thinnest and the damage is worst.

  • Own your test data. Create it in setup, clean it in teardown, and never depend on a record somebody added by hand last quarter.

  • Keep one expected result per check. Cases that assert six things at once fail without telling you which rule broke.

  • Fix or delete flaky tests promptly. A suite that goes red for no reason trains the team to ignore red.

The common mistakes mirror the list. Testing only the happy path, because it is the path that was demonstrated. Letting cases pile up until nobody knows which requirements are actually covered. Running the suite late, when the build is already a release candidate and a real failure means slipping the date. Chasing a coverage number instead of asking which rules are unverified.

Conclusion

Functional testing is a comparison: what the requirement promised against what the software did. Get the requirements testable, rank the features by what a failure costs, write cases with real data and one expected result each, and automate the ones you will run again. Assert the state after the action, not just the message on screen, and keep the suite small enough that a red build still means something. The worked case above is the shape to copy.

Frequently Asked Questions

What is functional testing in software testing?

Functional testing checks whether an application's features work as the requirements say they should. A tester provides an input or performs an action, then compares the actual result with the expected result. It covers units, integrations, APIs, full workflows and user acceptance.

What are the main types of functional testing?

Unit, component, integration, API, system or end-to-end, smoke, sanity, regression and acceptance testing. The first five describe how much of the system is assembled when the test runs. Smoke, sanity and regression describe when and why a set of tests is run.

How do you perform functional testing?

Read the requirements and remove the ambiguity, rank features by the cost of failure, then prepare an environment and controlled test data. Write cases with steps and one expected result each, execute against a known build, and compare actual with expected. Log the defects, retest each fix, and prune cases that no longer trace to a requirement.

How do you write a functional test case?

Give it an id, the requirement it traces to, preconditions, the exact test data, numbered steps, one expected result and a place to record the actual result and status. Assert the state after the action, such as the order count or the stock level, not only the message on screen. The worked checkout case above shows each field filled in.

Is functional testing black-box testing?

Often, but not always. Functional testing is often done through the interface without reading the code, which is black-box work. A unit test that verifies a pricing function from inside the codebase is white-box and still functional, because it compares behavior against a requirement. Sources: IBM and Applause, read 18 September 2026.

Can functional testing be automated?

Yes, and the repeatable parts should be. Automate stable flows that run often, such as checkout, API contracts and permission checks. Keep exploratory work, usability judgment and screens that are still changing with a human.

What is the difference between functional and non-functional testing?

Functional testing asks whether a feature works as specified. Non-functional testing assesses qualities such as performance, reliability, security, scalability and usability. Source: CircleCI, read 18 September 2026.

What is the difference between functional and regression testing?

Functional describes what is verified: behavior against requirements. Regression describes why tests are rerun: to confirm a change broke nothing that worked before. A regression suite can include functional tests you already wrote. See functional testing vs regression testing.

Who performs functional testing, and when?

Developers write unit and component tests as they build. QA engineers write and run integration, API and end-to-end tests once features are assembled. Product owners and end users handle acceptance testing before release. In a CI pipeline, the automated part runs on every pull request and deploy.

What tools are used for functional testing?

Playwright, Cypress and Selenium for web interfaces, Postman and REST Assured for APIs, JUnit, pytest and Jest for unit tests, Appium for mobile, TestComplete for Windows desktop, and Applitools for visual checks. Pick by the job rather than by a ranking; the tools table above pairs each job with its main tradeoff.

Ship continuously. Test continuously.

Qodex explores your app, writes runnable tests, and replays them on every change at zero LLM cost.