When someone says “we need to test this feature,” they could mean a dozen different things. Should you check whether the button works? Whether it works fast enough? Whether it works for users with screen readers? Whether it survives 10,000 simultaneous requests? The world of software testing is divided into two broad pillars: functional testing (does the software do what it is supposed to do?) and non-functional testing (does the software do it well enough?). Understanding this distinction is the first step toward building a test plan that covers quality from every angle.
Two Pillars of Software Quality
Functional testing verifies that the software behaves according to its requirements — the features, business rules, and user workflows. Non-functional testing verifies the quality attributes that determine whether the software is usable, performant, secure and reliable in real-world conditions.
# Functional vs Non-Functional testing comparison
TESTING_PILLARS = {
"Functional Testing": {
"question": "Does the software do what it is supposed to do?",
"focus": "Features, business logic, data processing, user workflows",
"examples": [
"Login with valid credentials returns a session token",
"Adding an item to the cart updates the cart count",
"Submitting a form with missing required fields shows validation errors",
"Discount code 'SAVE20' applies a 20% discount to the subtotal",
],
"derived_from": "Functional requirements and user stories",
"typical_types": [
"Unit testing",
"Integration testing",
"System testing",
"Acceptance testing (UAT)",
"Smoke testing",
"Regression testing",
],
},
"Non-Functional Testing": {
"question": "Does the software do it WELL ENOUGH?",
"focus": "Performance, security, usability, reliability, scalability",
"examples": [
"Login page loads within 2 seconds under normal traffic",
"System handles 5,000 concurrent users without degradation",
"Password is hashed with bcrypt, not stored in plain text",
"Application is navigable with keyboard-only and screen readers",
],
"derived_from": "Non-functional requirements, SLAs, industry standards",
"typical_types": [
"Performance testing (load, stress, endurance)",
"Security testing",
"Usability testing",
"Accessibility testing (WCAG)",
"Reliability / availability testing",
"Compatibility testing (browsers, devices)",
],
},
}
for pillar, info in TESTING_PILLARS.items():
print(f"\n{'='*55}")
print(f" {pillar}")
print(f"{'='*55}")
print(f" Core Question: {info['question']}")
print(f" Focus: {info['focus']}")
print(f" Derived from: {info['derived_from']}")
print(f"\n Examples:")
for ex in info['examples']:
print(f" - {ex}")
print(f"\n Includes:")
for t in info['typical_types']:
print(f" * {t}")
Common Mistakes
Mistake 1 — Treating functional testing as the only type of testing
❌ Wrong: “All test cases pass — the feature is ready for production.” (Only functional tests were run; no performance, security, or compatibility checks.)
✅ Correct: “All functional test cases pass. Performance testing shows p95 response time under 2 seconds. Security scan found no critical vulnerabilities. Cross-browser testing passed on Chrome, Firefox, and Safari. The feature is ready for production.”
Mistake 2 — Confusing testing types with testing levels
❌ Wrong: “Unit testing is a type of functional testing and performance testing is a type of non-functional testing, so they are completely separate.”
✅ Correct: “Testing types (functional, non-functional) describe WHAT you test. Testing levels (unit, integration, system) describe WHERE in the stack you test. Performance testing (non-functional type) can be done at the unit level, integration level, or system level.”