Skip to content

About

Advanced Mockito usage featuring Spies, Mocks, and behavioral verification to test a shopping cart checkout flow.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

Mockito: Mocks, Spies, and Verification

Overview

In high-stakes logic like a ShoppingCart checkout, it isn't enough to just check the return value. You need to ensure the PaymentService was actually contacted and that the internal list was modified correctly. This project demonstrates how to use Mocks for external dependencies and Spies for internal state tracking.


Core Concepts

1. Mock vs. Spy

This is a critical distinction in advanced testing:

  • @Mock (PaymentService): A complete "dummy" object. It has no real logic. If you don't stub a method, it returns null or false.
  • @Spy (List): A "partial mock." It wraps a real object (ArrayList). It calls the real methods (like add()) but allows you to track interactions and verify how many times those real methods were invoked.

2. Behavioral Verification

Verification moves the test from "What did I get?" to "What did you do?".

  • verify(paymentService).processPayment(100): Ensures the code actually called the payment service with the specific amount.
  • times(2): A verification mode that counts invocations. This is useful for ensuring loops or multi-step processes work as intended.
  • **anyString() / anyInt()**: Argument matchers that make tests flexible when the exact value doesn't matter, only the type does.

By using ShoppingCart as the System Under Test (SUT), you’ve demonstrated how to verify interactions and handle edge cases like exceptions.


1. High-Level Architecture: Mock vs. Spy

This diagram illustrates the internal state of the ShoppingCartTest. It highlights how Mockito manages the dependencies injected into the ShoppingCart.

Key Concepts:

  • @Mock (PaymentService): A completely hollow proxy. Unless you use when().thenReturn(), its methods do nothing (or return default values like false).
  • @Spy (List): A real ArrayList instance. It actually stores the items ("Apple", "Banana"), but Mockito "wraps" it so you can verify() how many times add() was called.
  • @InjectMocks (ShoppingCart): The real class we are testing. Mockito automatically passes the Mock and the Spy into its constructor.

2. Sequence Diagram: Successful Checkout

This flow traces the testCheckoutSuccessful method, showing how the "trained" mock responds to the call.

sequenceDiagram
    autonumber
    participant Test as ShoppingCartTest
    participant Cart as ShoppingCart (SUT)
    participant SpyList as items (ArrayList Spy)
    participant MockPay as paymentService (Mock)

    Note over Test, MockPay: === Setup ===
    Test->>MockPay: when(processPayment(100)).thenReturn(true)
    
    Note over Test, MockPay: === Execution ===
    Test->>SpyList: add("Apple")
    Test->>SpyList: add("Banana")
    Test->>Cart: checkout(100)
    activate Cart
    
    Cart->>SpyList: isEmpty()
    SpyList-->>Cart: false (Real ArrayList logic)
    
    Cart->>MockPay: processPayment(100)
    activate MockPay
    Note right of MockPay: Returns programmed value
    MockPay-->>Cart: true
    deactivate MockPay
    
    Cart-->>Test: true
    deactivate Cart

    Note over Test, MockPay: === Verification ===
    Test->>MockPay: verify processPayment(100) called?
    Test->>SpyList: verify add() called 2 times?

Loading

3. Comparison: Mockito Annotations

Annotation Type Behavior Best Use Case
@Mock Fake Hollow proxy; returns null, 0, or false. External dependencies (Databases, APIs).
@Spy Real + Tracking Calls real methods unless stubbed. Utilities or collections where internal state matters.
@InjectMocks Real Actual class being tested; dependencies injected. The "System Under Test" (SUT).

4. Technical Deep-Dive: Exception Handling

In your testCheckoutThrowsException method, you demonstrate Stubbing with Exceptions:

when(paymentService.processPayment(anyInt()))
    .thenThrow(new RuntimeException("Connection Lost"));

This is a critical part of robust testing. It ensures that if the PaymentService fails (network error, timeout, etc.), your ShoppingCart doesn't crash unexpectedly or, if it does, it's handled according to the business requirements.

Observation: In ShoppingCart.java, you don't have a try-catch block. This means the RuntimeException bubbles up directly to the caller, which is exactly what your assertThrows in the test verifies.


Component Reference

ShoppingCart.java

The "System Under Test" (SUT). It contains a guard clause (checking if the cart is empty) before delegating the heavy lifting to the PaymentService.

ShoppingCartTest.java

  • testCheckoutSuccessful: Demonstrates the full flow. It uses the Spy to track two item additions and the Mock to simulate a successful bank response.
  • testCheckoutThrowsException: Demonstrates "Negative Testing." We simulate a network failure (RuntimeException) and verify that our cart doesn't try to handle it incorrectly, but instead bubbles the exception up.

Key Takeaways

  • Interaction Testing: You aren't just testing the result; you are testing the contract between the ShoppingCart and the PaymentService.
  • Real Object Tracking: Using a @Spy on an ArrayList allows you to maintain the real behavior of the list while still having "spy cameras" on its methods.
  • Error Simulation: Mockito makes it trivial to simulate rare errors (like "Connection Lost") that are otherwise very difficult to trigger in a live environment.

PaymentService

  • Interface: Defines a contract but no implementation.
  • Method Signature: boolean processPayment(int amount) must be implemented by any class that uses this interface.
  • Return Type: boolean indicates success (true) or failure (false).
  • Purpose: Perfect for mocking in tests (e.g., using Mockito) because you can simulate payment success or failure without connecting to a real payment system.

ShoppingCart

  • Dependency Injection: PaymentService is passed into the constructor, making it easy to replace with a mock in tests.
  • Validation: The cart must have items before checkout.
  • Delegation: Payment responsibility is delegated to PaymentService.
  • Return Value: Checkout returns true if payment succeeds, false otherwise.

MockitoTestingApplication

  • Spring Boot Application: This is the "main class" that starts your project.
  • @SpringBootApplication: Combines configuration, auto-setup, and component scanning.
  • SpringApplication.run(): Actually starts the application and embedded server.
  • Purpose: Without this class, your Spring Boot app won’t launch.

MockitoTestingApplicationTests

  • @SpringBootTest: Boots up the entire Spring context for testing.

  • contextLoads() Test: A simple test that ensures the application starts correctly.

  • Purpose: This is often the first test in a Spring Boot project to confirm basic setup.

    Example Output when running tests:

    MockitoTestingApplicationTests ✔
        contextLoads ✔
    

ShoppingCartTest

  • @Mock: Creates fake dependencies (here, PaymentService).
  • @Spy: Wraps a real object but allows monitoring and partial mocking.
  • @InjectMocks: Injects mocks/spies into the class under test (ShoppingCart).
  • Mockito Behavior:
    • when(...).thenReturn(...): Defines mock return values.
    • when(...).thenThrow(...): Defines mock exceptions.
    • verify(...): Ensures methods were called with expected arguments.
  • JUnit Assertions:
    • assertTrue, assertFalse, assertThrows: Verify expected outcomes.

      Example Output when running tests:

      ShoppingCartTest ✔
          testCheckoutSuccessful ✔
          testCheckoutFailureDueToPayment ✔
          testCheckoutThrowsException ✔
      

Summary of Mockito Tools Used

Tool Purpose Use Case in Project
@Mock Create a fake dependency Simulating the PaymentService bank API.
@Spy Wrap a real object Tracking items added to the items list.
verify() Check if a method was called Ensuring the payment was actually triggered.
anyInt() Flexible matching Stubbing payments regardless of the specific amount.

About

Advanced Mockito usage featuring Spies, Mocks, and behavioral verification to test a shopping cart checkout flow.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages