Unit Testing Introduction
Unit Testing Introduction
Learn how unit testing checks small parts of a program, helps find bugs early, improves code quality, and gives developers confidence while changing code.
Introduction
Unit testing is the process of testing a small individual part of a program to check whether it works correctly.
A unit can be a function, method, class, module, or any small piece of logic that can be tested independently.
In real software development, unit testing is usually one of the first levels of testing. It helps developers catch problems early before the code is combined with other modules.
Easy Real-Life Example
Unit Testing as Checking Bicycle Parts
Before riding a bicycle, you may check each part separately: brakes, tyres, chain, bell, and seat. If each part works correctly, the complete bicycle is more likely to work safely.
Bicycle:
Check brake
Check tyre
Check chain
Check bell
Software:
Test login function
Test calculation function
Test validation function
Test search function
Unit testing works in the same way. It checks small parts before trusting the complete system.
What is a Unit?
A unit is the smallest testable part of a program.
The exact meaning of a unit depends on the programming language and project design, but commonly it means a function, method, class, or small module.
| Unit Type | Meaning | Example |
|---|---|---|
| Function | A reusable block of logic. | calculateTotal() |
| Method | A function inside a class or object. | student.getGrade() |
| Class | A blueprint containing data and behavior. | ShoppingCart |
| Module | A group of related logic. | marksUtilities |
What is Unit Testing?
Unit testing checks whether a single unit of code produces the expected result for given input.
It usually compares the actual output of the unit with the expected output.
Simple Example
Function:
isPassingMark(marks)
Requirement:
Return true if marks are 40 or above.
Input:
marks = 50
Expected Output:
true
Actual Output:
true
Result:
Test Passed
Why Unit Testing is Important
Unit testing is important because it helps find bugs early in the development process.
When a bug is found in a small function, it is usually easier to understand and fix than a bug found after the complete system is built.
Benefits of Unit Testing
- Finds bugs early.
- Makes debugging easier.
- Improves code quality.
- Encourages small and focused functions.
- Gives confidence while changing code.
- Helps prevent old bugs from returning.
- Makes refactoring safer.
- Acts as basic documentation for expected behavior.
- Supports automated testing.
- Reduces risk before integration testing.
Unit Testing in the Testing Levels
Unit testing is usually the first level of software testing.
Unit Testing
↓
Integration Testing
↓
System Testing
↓
Acceptance Testing
Unit testing checks individual parts. Integration testing checks how parts work together. System testing checks the full system. Acceptance testing checks whether the users accept the software.
Example: Function to Test
Suppose we have a function that calculates whether a student passes or fails.
FUNCTION isPassingMark(marks)
IF marks >= 40 THEN
RETURN true
ELSE
RETURN false
END IF
END FUNCTION
We can write unit tests for this function using different inputs.
Unit Test Cases for the Function
| Test Case | Input | Expected Output | Reason |
|---|---|---|---|
| TC_001 | marks = 50 |
true |
Marks are above passing mark. |
| TC_002 | marks = 40 |
true |
Boundary value for passing. |
| TC_003 | marks = 39 |
false |
Just below passing mark. |
| TC_004 | marks = 0 |
false |
Lowest possible valid mark. |
Unit Testing Process
Unit testing can be followed as a simple step-by-step process.
1. Identify the unit to test
2. Understand expected behavior
3. Prepare test data
4. Execute the unit
5. Compare actual result with expected result
6. Mark test as pass or fail
7. Fix defects if needed
8. Re-run the test
Expected Result vs Actual Result
Unit testing compares what should happen with what actually happens.
| Concept | Meaning | Example |
|---|---|---|
| Expected Result | The result that should happen. | For marks 50, expected result is Pass. |
| Actual Result | The result produced by the code. | The function returns Pass. |
| Test Status | Pass if both match, fail if they do not. | Pass when expected and actual are same. |
AAA Pattern in Unit Testing
A common way to structure unit tests is the AAA pattern.
AAA stands for Arrange, Act, and Assert.
| AAA Step | Meaning | Example |
|---|---|---|
| Arrange | Prepare input data and required setup. | marks = 50 |
| Act | Call the function or unit being tested. | result = isPassingMark(marks) |
| Assert | Check whether actual result matches expected result. | result should be true |
AAA Example in Pseudocode
TEST "Marks 50 should return true"
// Arrange
marks = 50
expectedResult = true
// Act
actualResult = isPassingMark(marks)
// Assert
CHECK actualResult == expectedResult
END TEST
The AAA pattern makes tests clean, readable, and easy to understand.
Positive, Negative, and Boundary Unit Tests
Good unit tests should check different types of scenarios.
| Test Type | Meaning | Example |
|---|---|---|
| Positive Test | Checks valid expected behavior. | Marks 80 returns Pass. |
| Negative Test | Checks invalid or wrong input behavior. | Marks -5 returns Invalid marks. |
| Boundary Test | Checks values at the edge of allowed range. | Marks 40 and 39. |
| Exception Test | Checks whether error conditions are handled. | Empty input throws validation error. |
Boundary Values in Unit Testing
Boundary values are very important because many bugs happen near limits.
Requirement:
Marks must be from 0 to 100.
Important unit test values:
-1 → invalid
0 → valid
1 → valid
39 → fail
40 → pass
100 → valid
101 → invalid
These values help test both valid and invalid edges.
Manual Unit Testing vs Automated Unit Testing
Unit testing can be done manually, but in professional projects it is commonly automated using testing frameworks.
| Manual Unit Testing | Automated Unit Testing |
|---|---|
| Developer checks function output manually. | A test script checks output automatically. |
| Useful for quick learning and debugging. | Useful for repeated testing and large projects. |
| Can be slow and inconsistent. | Fast, repeatable, and reliable. |
| Hard to run many tests again and again. | Many tests can run automatically after code changes. |
Unit Testing Frameworks
A unit testing framework helps developers write, run, and organize unit tests.
Different programming languages have different testing frameworks.
| Language / Ecosystem | Common Testing Framework Examples |
|---|---|
| Java | JUnit, TestNG |
| Python | unittest, pytest |
| JavaScript | Jest, Mocha, Jasmine |
| .NET | xUnit, NUnit, MSTest |
| PHP | PHPUnit |
The concept of unit testing is language-neutral, even if the tools are different.
Example: Unit Testing a Calculator Function
Function
FUNCTION addNumbers(firstNumber, secondNumber)
RETURN firstNumber + secondNumber
END FUNCTION
Unit Tests
TEST "Add two positive numbers"
// Arrange
firstNumber = 5
secondNumber = 3
expectedResult = 8
// Act
actualResult = addNumbers(firstNumber, secondNumber)
// Assert
CHECK actualResult == expectedResult
END TEST
TEST "Add positive and negative number"
// Arrange
firstNumber = 5
secondNumber = -3
expectedResult = 2
// Act
actualResult = addNumbers(firstNumber, secondNumber)
// Assert
CHECK actualResult == expectedResult
END TEST
These tests verify that the addition function works for different inputs.
Real-World Example: Shopping Cart Unit Tests
In an e-commerce application, a shopping cart may have many small units to test.
| Unit | Example Unit Test |
|---|---|
addItem() |
Add one product and verify cart quantity. |
calculateSubtotal() |
Verify total price before discount and tax. |
applyDiscount() |
Apply 10% discount and verify final amount. |
clearCart() |
Clear all items and verify cart becomes empty. |
validateQuantity() |
Reject quantity less than 1. |
Student-Friendly Example: Grade Calculator Unit Tests
Function:
calculateGrade(marks)
Rules:
90 to 100 → A
75 to 89 → B
60 to 74 → C
40 to 59 → D
Below 40 → Fail
Invalid if marks are below 0 or above 100
Unit Test Cases:
marks = 95 → A
marks = 75 → B
marks = 60 → C
marks = 40 → D
marks = 39 → Fail
marks = -1 → Invalid
marks = 101 → Invalid
This example includes normal values, boundary values, and invalid values.
Unit Tests Should Be Isolated
A unit test should focus on one unit and avoid depending on unrelated systems.
If a unit test depends on a database, external API, network, or full user interface, it may become slow and unreliable.
Characteristics of Good Unit Tests
Good unit tests should be easy to run, easy to understand, and reliable.
Good Unit Tests Should Be
- Small: Test one behavior at a time.
- Fast: Run quickly so developers can run them often.
- Independent: Do not depend on other tests.
- Repeatable: Same input should produce same result.
- Readable: Easy to understand from the test name and structure.
- Automated: Can be executed by a testing tool.
- Focused: Test one clear condition or behavior.
What Unit Testing Can and Cannot Find
Unit testing is powerful, but it does not replace all other testing.
| Unit Testing Can Find | Unit Testing May Not Find |
|---|---|
| Wrong calculation logic. | Problems between multiple modules. |
| Incorrect validation rule. | Full system workflow issues. |
| Boundary value mistakes. | User interface layout issues. |
| Unexpected function output. | Real database or network integration problems. |
That is why unit testing should be combined with integration, system, acceptance, and regression testing.
Unit Testing and Regression Safety
Unit tests help protect existing behavior when code changes.
If a developer refactors a function and accidentally breaks it, a unit test can fail immediately and alert the developer.
Before change:
calculateDiscount(1000, 10) → 100
After refactoring:
Unit test still checks:
calculateDiscount(1000, 10) should return 100
If result changes unexpectedly:
Test fails
This gives developers confidence when improving or refactoring code.
Unit Testing and TDD
TDD stands for Test-Driven Development.
In TDD, developers write tests before writing the actual implementation code.
TDD Flow:
1. Write a failing test
2. Write minimum code to pass the test
3. Refactor the code
4. Run tests again
Beginners do not need to master TDD immediately, but they should understand that unit tests can also guide software design.
Common Beginner Mistakes in Unit Testing
Mistakes
- Testing only one happy path.
- Ignoring negative and boundary cases.
- Writing unclear test names.
- Testing too many things in one test.
- Depending on external systems unnecessarily.
- Not updating tests when requirements change.
- Not running tests after code changes.
- Writing tests that are difficult to understand.
Better Habits
- Test one behavior at a time.
- Use clear test names.
- Follow Arrange, Act, Assert.
- Include positive, negative, and boundary cases.
- Keep tests independent.
- Run tests after every important change.
- Write simple and readable tests.
- Fix failing tests carefully.
Best Practices for Unit Testing
Recommended Practices
- Write tests for important business logic.
- Use meaningful test names.
- Follow the AAA pattern.
- Keep each test small and focused.
- Test normal, invalid, and boundary inputs.
- Make tests independent from each other.
- Avoid unnecessary external dependencies.
- Run unit tests frequently.
- Use automated testing frameworks when possible.
- Review failed tests carefully.
- Update tests when requirements change.
- Use unit tests as a safety net during refactoring.
Prerequisites Before Learning Unit Testing
Students should understand the following topics before learning unit testing deeply:
Required Knowledge
- Basic programming syntax.
- Variables and data types.
- Control flow and loops.
- Functions or methods.
- Return values.
- Input and output concepts.
- Testing basics.
- Clean code basics.
- Debugging basics.
Trace Table Example: Unit Testing a Discount Function
FUNCTION calculateDiscount(amount, discountPercentage)
RETURN amount * discountPercentage / 100
END FUNCTION
| Test Case | Amount | Discount % | Expected Discount |
|---|---|---|---|
| TC_001 | 1000 | 10 | 100 |
| TC_002 | 500 | 0 | 0 |
| TC_003 | 200 | 50 | 100 |
| TC_004 | 0 | 10 | 0 |
Practice Activity: Write Unit Tests
Write unit test cases for the following function.
FUNCTION isEven(number)
IF number MOD 2 == 0 THEN
RETURN true
ELSE
RETURN false
END IF
END FUNCTION
Sample Unit Test Cases
Test Case 1:
number = 2
Expected Result: true
Test Case 2:
number = 3
Expected Result: false
Test Case 3:
number = 0
Expected Result: true
Test Case 4:
number = -4
Expected Result: true
Test Case 5:
number = -7
Expected Result: false
Mini Quiz
What is unit testing?
Unit testing is the process of testing a small individual part of code, such as a function or method, to check whether it works correctly.
What is a unit in unit testing?
A unit is the smallest testable part of a program, such as a function, method, class, or module.
What does AAA stand for?
AAA stands for Arrange, Act, and Assert.
Why is unit testing useful?
Unit testing is useful because it finds bugs early, makes debugging easier, improves code quality, and gives confidence during code changes.
What should a good unit test check?
A good unit test should check one clear behavior using valid, invalid, and boundary input where appropriate.
Interview Questions on Unit Testing Introduction
What is the purpose of unit testing?
The purpose of unit testing is to verify that individual units of code work correctly before they are combined with other parts of the system.
What is the difference between unit testing and integration testing?
Unit testing checks one small unit in isolation, while integration testing checks whether multiple units or modules work correctly together.
Why should unit tests be independent?
Unit tests should be independent so that one test does not depend on another test’s result or execution order.
What is the AAA pattern?
The AAA pattern is a structure for unit tests where Arrange prepares data, Act executes the unit, and Assert checks the expected result.
Why are boundary cases important in unit testing?
Boundary cases are important because many bugs occur near minimum, maximum, or decision-changing values.
Quick Summary
| Concept | Meaning |
|---|---|
| Unit Testing | Testing a small individual part of code. |
| Unit | A function, method, class, or module that can be tested. |
| Expected Result | The result that should happen. |
| Actual Result | The result produced by the code. |
| AAA Pattern | Arrange, Act, Assert structure for writing tests. |
| Boundary Test | Testing values near limits or decision points. |
| Automated Unit Test | A test executed automatically by a testing framework. |
| Regression Safety | Unit tests help detect if old behavior breaks after changes. |
Final Takeaway
Unit testing is a foundational software testing practice where small parts of code are tested independently. It helps developers find bugs early, improve code quality, test boundary cases, and safely change or refactor code. A good unit test is small, fast, independent, readable, and focused on one behavior. Students should learn to write unit tests using clear test cases, expected results, and the Arrange-Act-Assert pattern.