Bird
Raised Fist0
Testing Fundamentalstesting~15 mins

Unit testing in Testing Fundamentals - Deep Dive

Choose your learning style10 modes available
Overview - Unit testing
What is it?
Unit testing is a way to check small parts of a program, called units, to make sure they work correctly by themselves. Each unit is tested separately to find mistakes early. It helps developers confirm that each piece does what it should before combining them. This makes fixing problems easier and faster.
Why it matters
Without unit testing, bugs can hide in small parts of code and cause bigger problems later, making software unreliable and costly to fix. Unit testing saves time and money by catching errors early and helps keep code safe when changes are made. It builds confidence that the software works as expected.
Where it fits
Before learning unit testing, you should understand basic programming and how software is built. After unit testing, you can learn about integration testing and system testing, which check how parts work together and how the whole software behaves.
Mental Model
Core Idea
Unit testing is like checking each brick in a wall before building the whole wall to ensure the final structure is strong and reliable.
Think of it like...
Imagine baking a cake by testing each ingredient separately—like tasting the batter before baking—to make sure each part is good before mixing everything together.
┌───────────────┐
│   Software    │
│   System      │
└──────┬────────┘
       │
┌──────┴───────┐
│   Modules    │
└──────┬───────┘
       │
┌──────┴───────┐
│    Units     │
│ (Functions)  │
└──────────────┘

Unit Testing checks each box at the bottom before moving up.
Build-Up - 7 Steps
1
FoundationWhat is a Unit Test?
🤔
Concept: Introduce the basic idea of testing a single small piece of code independently.
A unit test checks one small part of your code, like a single function or method. It runs that part with some inputs and checks if the output is what you expect. For example, testing a function that adds two numbers by giving it 2 and 3 and expecting 5.
Result
You get a clear yes or no answer if that small part works correctly.
Understanding that testing small pieces separately helps find problems quickly and keeps code reliable.
2
FoundationWhy Write Unit Tests?
🤔
Concept: Explain the purpose and benefits of unit testing in software development.
Unit tests help catch mistakes early before they cause bigger problems. They make it safer to change code because you can check if changes break anything. They also serve as documentation showing how code is supposed to work.
Result
Developers spend less time debugging and more time building new features confidently.
Knowing the benefits motivates writing tests and improves software quality.
3
IntermediateWriting Your First Unit Test
🤔Before reading on: do you think a unit test should test multiple functions at once or just one? Commit to your answer.
Concept: Learn how to write a simple unit test for a single function using a testing framework.
Pick a function, for example, add(a, b) that returns a + b. Write a test that calls add(2, 3) and checks if the result is 5. Use a testing tool like unittest in Python or JUnit in Java to run this test automatically.
Result
The test passes if the function returns 5, fails otherwise.
Understanding that tests should be small and focused on one thing makes debugging easier.
4
IntermediateTest Assertions and Expected Results
🤔Before reading on: do you think a test should only check if output equals expected, or also check other conditions like errors? Commit to your answer.
Concept: Introduce assertions, which are checks inside tests that confirm expected behavior, including outputs and error handling.
Assertions compare actual results to expected ones. For example, assert that add(2, 3) equals 5. You can also check if a function raises an error when given bad input. Assertions tell the test runner if the test passed or failed.
Result
Tests give clear feedback on what works and what doesn't.
Knowing how to write good assertions helps catch subtle bugs and improves test usefulness.
5
IntermediateIsolating Units with Mocks and Stubs
🤔Before reading on: do you think unit tests should depend on databases or external services? Commit to your answer.
Concept: Explain how to isolate the unit under test by replacing parts it depends on with simple fake versions called mocks or stubs.
If a function calls a database, use a mock to simulate the database response instead of the real one. This keeps tests fast and focused on the unit itself. Mocks can check if the unit calls dependencies correctly.
Result
Tests run quickly and only fail if the unit itself has a problem.
Understanding isolation prevents tests from breaking due to unrelated parts and makes debugging easier.
6
AdvancedOrganizing and Naming Unit Tests
🤔Before reading on: do you think test names should be short and vague or descriptive and clear? Commit to your answer.
Concept: Learn best practices for structuring test files and naming tests to make them easy to understand and maintain.
Group tests by the unit they test, use clear names like test_add_two_positive_numbers, and keep tests independent. Good organization helps teams find and fix problems faster.
Result
A clean, understandable test suite that grows with the project.
Knowing how to organize tests saves time and reduces confusion in real projects.
7
ExpertWhen Unit Tests Fail: Debugging and Flaky Tests
🤔Before reading on: do you think all test failures mean your code is broken? Commit to your answer.
Concept: Explore causes of test failures, including flaky tests that sometimes pass and sometimes fail, and how to diagnose them.
Not all failures mean bugs in code; some tests fail due to timing, environment, or dependencies. Flaky tests reduce trust in testing. Techniques like test isolation, retries, and stable test data help fix this.
Result
More reliable tests and faster problem solving.
Understanding test failures deeply prevents wasted time chasing false alarms and improves test quality.
Under the Hood
Unit tests run code in isolation, often in a special environment that resets before each test. The test runner executes each test function, captures results, and reports pass or fail based on assertions. Mocks replace real dependencies by intercepting calls and returning controlled responses. This isolation ensures tests only check the intended unit.
Why designed this way?
Unit testing was designed to catch bugs early and make debugging easier by focusing on small code parts. Early software development had costly bugs found late, so breaking code into testable units improved quality and speed. Alternatives like manual testing or only system testing were slower and less precise.
┌───────────────┐
│ Test Runner   │
├───────────────┤
│ Executes Test │
│ Functions     │
├───────────────┤
│ Captures      │
│ Assertions    │
├───────────────┤
│ Reports Pass/ │
│ Fail          │
└───────┬───────┘
        │
┌───────┴─────────────┐
│ Unit Under Test      │
│ (Function/Method)   │
└─────────┬───────────┘
          │
┌─────────┴───────────┐
│ Mocks/Stubs         │
│ (Fake Dependencies) │
└─────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do unit tests guarantee the entire software is bug-free? Commit to yes or no.
Common Belief:Unit tests ensure the whole software has no bugs once they all pass.
Tap to reveal reality
Reality:Unit tests only check small parts in isolation; they cannot catch bugs in how parts work together or in overall system behavior.
Why it matters:Relying only on unit tests can miss integration bugs, leading to failures in real use.
Quick: Should unit tests be slow and test everything deeply? Commit to yes or no.
Common Belief:Unit tests should test every detail and can be slow because thoroughness is key.
Tap to reveal reality
Reality:Unit tests must be fast and focused; slow tests reduce developer productivity and discourage frequent runs.
Why it matters:Slow tests lead to less frequent testing and more bugs slipping through.
Quick: Can unit tests depend on real databases or external services safely? Commit to yes or no.
Common Belief:Using real databases in unit tests is fine and makes tests more realistic.
Tap to reveal reality
Reality:Unit tests should avoid real external dependencies to stay fast and reliable; mocks or stubs should replace them.
Why it matters:Tests depending on real services can fail unpredictably and slow down development.
Quick: Do all test failures mean the code is broken? Commit to yes or no.
Common Belief:If a test fails, the code under test must have a bug.
Tap to reveal reality
Reality:Tests can fail due to flaky behavior, environment issues, or test mistakes, not just code bugs.
Why it matters:Misinterpreting failures wastes time chasing non-existent bugs and reduces trust in tests.
Expert Zone
1
Tests should be designed to run independently and in any order to avoid hidden dependencies that cause flaky failures.
2
Mocking too much can hide real problems; balance is needed between isolation and realistic testing.
3
Good unit tests serve as living documentation, helping new developers understand code behavior quickly.
When NOT to use
Unit testing is not suitable for testing user interfaces, performance, or how multiple components work together. For these, use integration, system, or acceptance testing instead.
Production Patterns
In real projects, unit tests are integrated into automated pipelines that run on every code change. Teams use code coverage tools to measure how much code is tested and focus on critical paths. Tests are often grouped by feature or module for easier maintenance.
Connections
Integration Testing
Builds-on
Understanding unit testing helps grasp integration testing, which checks how units work together, extending the idea of small checks to combined parts.
Continuous Integration (CI)
Supports
Unit tests are essential in CI pipelines to automatically verify code changes quickly, enabling faster and safer software delivery.
Scientific Method
Shares pattern
Unit testing follows the scientific method by forming hypotheses (expected behavior), running experiments (tests), and observing results, showing how testing in software mirrors scientific thinking.
Common Pitfalls
#1Writing tests that depend on each other, causing failures when run in different orders.
Wrong approach:def test_a(): global shared_state shared_state = 1 def test_b(): assert shared_state == 1
Correct approach:def test_a(): state = 1 assert state == 1 def test_b(): state = 1 assert state == 1
Root cause:Misunderstanding that tests must be independent and not share or rely on external state.
#2Using real external services in unit tests, making tests slow and unreliable.
Wrong approach:def test_fetch_data(): data = fetch_from_real_database() assert data is not None
Correct approach:def test_fetch_data(): mock_db = MockDatabase() mock_db.set_return_value({'key': 'value'}) data = fetch_from_database(mock_db) assert data == {'key': 'value'}
Root cause:Not isolating the unit under test and misunderstanding the purpose of mocks.
#3Writing vague test names that do not explain what is tested.
Wrong approach:def test1(): assert add(2, 3) == 5
Correct approach:def test_add_two_positive_numbers(): assert add(2, 3) == 5
Root cause:Underestimating the importance of clear communication in test code.
Key Takeaways
Unit testing checks small parts of code independently to catch bugs early and make debugging easier.
Good unit tests are fast, isolated, and focused on one behavior at a time.
Using mocks and stubs helps isolate units by replacing external dependencies.
Clear test organization and naming improve maintainability and team collaboration.
Unit tests are a foundation for higher-level testing and continuous integration in professional software development.

Practice

(1/5)
1. What is the main purpose of unit testing in software development?
easy
A. To deploy the software to users
B. To test the entire software system at once
C. To check small parts of code work correctly
D. To design the user interface

Solution

  1. Step 1: Understand unit testing scope

    Unit testing focuses on testing small, individual parts of code, like functions or methods.
  2. Step 2: Compare with other testing types

    Testing the entire system is integration or system testing, not unit testing.
  3. Final Answer:

    To check small parts of code work correctly -> Option C
  4. Quick Check:

    Unit testing = small code parts check [OK]
Hint: Unit tests check small code parts, not whole system [OK]
Common Mistakes:
  • Confusing unit testing with system testing
  • Thinking unit tests check UI or deployment
  • Believing unit tests run the full application
2. Which of the following is the correct syntax for a simple unit test assertion in Python using assert?
easy
A. assert add(2, 3) == 5
B. assert add(2, 3) = 5
C. assert(add(2, 3) = 5)
D. assert add(2, 3) equals 5

Solution

  1. Step 1: Recall Python assert syntax

    Python uses assert expression where expression must be True for test to pass.
  2. Step 2: Check each option

    assert add(2, 3) == 5 uses correct syntax: assert add(2, 3) == 5. assert add(2, 3) = 5 uses single = which is assignment, invalid here. assert(add(2, 3) = 5) uses single = which is assignment, invalid syntax. assert add(2, 3) equals 5 uses invalid keyword 'equals'.
  3. Final Answer:

    assert add(2, 3) == 5 -> Option A
  4. Quick Check:

    assert with == for comparison [OK]
Hint: Use 'assert expression' with '==' for comparison in Python [OK]
Common Mistakes:
  • Using single '=' instead of '==' in assert
  • Writing assert with invalid keywords
  • Adding unnecessary parentheses in assert
3. Given the Python function and test below, what will be the test result?
def multiply(a, b):
    return a * b

assert multiply(3, 4) == 12
assert multiply(0, 5) == 0
assert multiply(-1, 3) == -3
assert multiply(2, 2) == 5
medium
A. The last assertion fails
B. All assertions pass
C. The first assertion fails
D. The second assertion fails

Solution

  1. Step 1: Evaluate each assertion

    multiply(3,4) = 12 matches expected 12 (pass). multiply(0,5) = 0 matches 0 (pass). multiply(-1,3) = -3 matches -3 (pass). multiply(2,2) = 4 but expected 5 (fail).
  2. Step 2: Identify failing assertion

    The last assertion expects 5 but function returns 4, so it fails.
  3. Final Answer:

    The last assertion fails -> Option A
  4. Quick Check:

    4 != 5 causes failure [OK]
Hint: Check each assert expected vs actual carefully [OK]
Common Mistakes:
  • Assuming all assertions pass without checking values
  • Confusing multiplication results
  • Ignoring the last assertion
4. Identify the error in the following Python unit test code:
def divide(a, b):
    return a / b

assert divide(10, 2) == 5
assert divide(5, 0) == 0
medium
A. The function should return integer only
B. Division by zero causes runtime error
C. The first assertion is incorrect
D. Assertions are missing parentheses

Solution

  1. Step 1: Analyze function behavior

    Function divides a by b. Dividing by zero is not allowed and causes an error.
  2. Step 2: Check assertions

    First assertion divides 10 by 2, which is valid. Second assertion divides 5 by 0, which causes a runtime ZeroDivisionError.
  3. Final Answer:

    Division by zero causes runtime error -> Option B
  4. Quick Check:

    Divide by zero error in second assert [OK]
Hint: Watch for zero division in tests [OK]
Common Mistakes:
  • Ignoring division by zero error
  • Thinking assertions need parentheses in Python
  • Assuming function returns only integers
5. You want to write a unit test for a function that returns the maximum number from a list. Which test case below best checks the function handles an empty list correctly?
hard
A. assert max_number([-1, -2]) == -1
B. assert max_number([1, 2, 3]) == 3
C. assert max_number([0]) == 0
D. assert max_number([]) == None

Solution

  1. Step 1: Understand the edge case

    An empty list has no maximum value, so the function should handle this gracefully, often by returning None or raising an error.
  2. Step 2: Evaluate test cases

    assert max_number([]) == None tests the empty list case expecting None, which is appropriate. Other options test normal lists, not empty.
  3. Final Answer:

    assert max_number([]) == None -> Option D
  4. Quick Check:

    Empty list test expects None [OK]
Hint: Test empty inputs with None or error expected [OK]
Common Mistakes:
  • Not testing empty list edge case
  • Using normal list tests for empty case
  • Assuming empty list returns zero
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard