Basic Unit Test Structure

Java (JUnit 4): java import org.junit.Test; import org.junit.Before; import org.junit.After; import static org.junit.Assert.;

TrackJava to Python Journey
Current SectionPython Testing
Progress19 of 19

1. Basic Unit Test Structure

Java (JUnit 4):

import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import static org.junit.Assert.*;

public class CalculatorTest {
    private Calculator calculator;
    
    @Before  // Runs before each test
    public void setUp() {
        calculator = new Calculator();
    }
    
    @After   // Runs after each test
    public void tearDown() {
        calculator = null;
    }
    
    @Test
    public void testAddition() {
        int result = calculator.add(5, 3);
        assertEquals(8, result);
    }
    
    @Test
    public void testSubtraction() {
        int result = calculator.subtract(10, 3);
        assertEquals(7, result);
    }
}

Python (unittest):

import unittest

class Calculator:
    def add(self, a, b):
        return a + b
    
    def subtract(self, a, b):
        return a - b

class CalculatorTest(unittest.TestCase):
    def setUp(self):  # Runs before each test
        self.calculator = Calculator()
    
    def tearDown(self):  # Runs after each test
        self.calculator = None
    
    def test_addition(self):
        result = self.calculator.add(5, 3)
        self.assertEqual(result, 8)
    
    def test_subtraction(self):
        result = self.calculator.subtract(10, 3)
        self.assertEqual(result, 7)

# Run tests
if __name__ == "__main__":
    unittest.main()

Python (pytest - Modern, Recommended):

import pytest

class Calculator:
    def add(self, a, b):
        return a + b
    
    def subtract(self, a, b):
        return a - b

class TestCalculator:
    @pytest.fixture(autouse=True)
    def setup(self):
        self.calculator = Calculator()
        yield
        self.calculator = None
    
    def test_addition(self):
        result = self.calculator.add(5, 3)
        assert result == 8
    
    def test_subtraction(self):
        result = self.calculator.subtract(10, 3)
        assert result == 7

# Run: pytest test_calculator.py

Key Differences:

  • Java uses @Test, @Before, @After annotations.
  • Python's unittest uses methods like setUp(), tearDown().
  • pytest is more concise with simpler assertion syntax.
  • pytest is generally preferred for Python projects.

2. Assertions Comparison

  • Equal

    • Java (JUnit): assertEquals(expected, actual)
    • Python (unittest): self.assertEqual(actual, expected)
    • Python (pytest): assert actual == expected
  • Not Equal

    • Java (JUnit): assertNotEquals(a, b)
    • Python (unittest): self.assertNotEqual(a, b)
    • Python (pytest): assert a != b
  • True

    • Java (JUnit): assertTrue(condition)
    • Python (unittest): self.assertTrue(condition)
    • Python (pytest): assert condition
  • False

    • Java (JUnit): assertFalse(condition)
    • Python (unittest): self.assertFalse(condition)
    • Python (pytest): assert not condition
  • Null

    • Java (JUnit): assertNull(obj)
    • Python (unittest): self.assertIsNone(obj)
    • Python (pytest): assert obj is None
  • Not Null

    • Java (JUnit): assertNotNull(obj)
    • Python (unittest): self.assertIsNotNone(obj)
    • Python (pytest): assert obj is not None
  • Contains

    • Java (JUnit): assertTrue(list.contains(item))
    • Python (unittest): self.assertIn(item, list)
    • Python (pytest): assert item in list
  • Exception

    • Java (JUnit): @Test(expected=Exception.class)
    • Python (unittest): self.assertRaises(Exception, ...)
    • Python (pytest): with pytest.raises(Exception):

3. Testing Exceptions

Java:

@Test(expected = IllegalArgumentException.class)
public void testInvalidInput() {
    calculator.divide(10, 0);
}

// Or using ExpectedException rule
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testDivisionByZero() {
    thrown.expect(IllegalArgumentException.class);
    thrown.expectMessage("Cannot divide by zero");
    calculator.divide(10, 0);
}

Python (unittest):

def test_division_by_zero(self):
    with self.assertRaises(ValueError):
        calculator.divide(10, 0)

def test_exception_message(self):
    with self.assertRaises(ValueError) as context:
        calculator.divide(10, 0)
    self.assertIn("Cannot divide by zero", str(context.exception))

Python (pytest):

def test_division_by_zero():
    with pytest.raises(ValueError):
        calculator.divide(10, 0)

def test_exception_message():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        calculator.divide(10, 0)

4. Parametrized Tests

Java (JUnit 4):

import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class CalculatorParameterizedTest {
    private int a;
    private int b;
    private int expected;
    
    public CalculatorParameterizedTest(int a, int b, int expected) {
        this.a = a;
        this.b = b;
        this.expected = expected;
    }
    
    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
            { 5, 3, 8 },
            { 10, 20, 30 },
            { -5, 5, 0 }
        });
    }
    
    @Test
    public void testAddition() {
        Calculator calc = new Calculator();
        assertEquals(expected, calc.add(a, b));
    }
}

Python (pytest):

import pytest

class TestCalculator:
    @pytest.mark.parametrize("a,b,expected", [
        (5, 3, 8),
        (10, 20, 30),
        (-5, 5, 0)
    ])
    def test_addition(self, a, b, expected):
        calculator = Calculator()
        assert calculator.add(a, b) == expected

Python (unittest):

import unittest
from parameterized import parameterized

class TestCalculator(unittest.TestCase):
    @parameterized.expand([
        (5, 3, 8),
        (10, 20, 30),
        (-5, 5, 0)
    ])
    def test_addition(self, a, b, expected):
        calculator = Calculator()
        self.assertEqual(calculator.add(a, b), expected)

5. Mocking Basics

Java (Mockito):

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.*;

public class OrderServiceTest {
    @Mock
    private PaymentService paymentService;
    
    private OrderService orderService;
    
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        orderService = new OrderService(paymentService);
    }
    
    @Test
    public void testOrderCreation() {
        // Setup mock
        when(paymentService.charge(100.0)).thenReturn(true);
        
        // Execute
        boolean result = orderService.createOrder(100.0);
        
        // Verify
        assertTrue(result);
        verify(paymentService).charge(100.0);
    }
}

Python (unittest.mock):

from unittest.mock import Mock, patch, MagicMock

class PaymentService:
    def charge(self, amount):
        pass

class OrderService:
    def __init__(self, payment_service):
        self.payment_service = payment_service
    
    def create_order(self, amount):
        return self.payment_service.charge(amount)

class TestOrderService(unittest.TestCase):
    def setUp(self):
        self.payment_service = Mock()
        self.order_service = OrderService(self.payment_service)
    
    def test_order_creation(self):
        # Setup mock
        self.payment_service.charge.return_value = True
        
        # Execute
        result = self.order_service.create_order(100.0)
        
        # Verify
        self.assertTrue(result)
        self.payment_service.charge.assert_called_once_with(100.0)

Python (pytest with unittest.mock):

from unittest.mock import Mock, patch

def test_order_creation():
    # Setup mock
    payment_service = Mock()
    payment_service.charge.return_value = True
    
    order_service = OrderService(payment_service)
    
    # Execute
    result = order_service.create_order(100.0)
    
    # Verify
    assert result is True
    payment_service.charge.assert_called_once_with(100.0)

6. Mock Verification

Java (Mockito):

@Test
public void testMockVerification() {
    UserService userService = mock(UserService.class);
    
    // Setup
    when(userService.getUser("alice")).thenReturn(new User("alice", "Alice"));
    
    // Execute
    User user = userService.getUser("alice");
    
    // Verify method was called
    verify(userService).getUser("alice");
    
    // Verify called exactly once
    verify(userService, times(1)).getUser("alice");
    
    // Verify never called
    verify(userService, never()).getUser("bob");
    
    // Verify called at least once
    verify(userService, atLeastOnce()).getUser("alice");
}

Python (unittest.mock):

def test_mock_verification():
    user_service = Mock()
    
    # Setup
    user_service.get_user.return_value = {"name": "alice"}
    
    # Execute
    user = user_service.get_user("alice")
    
    # Verify method was called
    user_service.get_user.assert_called_with("alice")
    
    # Verify called exactly once
    user_service.get_user.assert_called_once_with("alice")
    
    # Verify called N times
    assert user_service.get_user.call_count == 1
    
    # Verify never called
    user_service.get_other_user.assert_not_called()

7. Patching and Spying

Java (Mockito with Spy):

@Test
public void testSpying() {
    List<String> realList = new ArrayList<>();
    List<String> spyList = spy(realList);
    
    // Real behavior
    spyList.add("item1");
    assertEquals(1, spyList.size());
    
    // Mock specific method
    when(spyList.get(0)).thenReturn("mocked");
    assertEquals("mocked", spyList.get(0));
    
    // Verify was called
    verify(spyList).add("item1");
}

Python (unittest.mock.patch):

from unittest.mock import patch, MagicMock

# Patch a module function
@patch("module.external_api")
def test_with_patch(mock_api):
    mock_api.return_value = "mocked result"
    
    result = my_function()
    
    assert result == "mocked result"
    mock_api.assert_called_once()

# Or using context manager
def test_with_patch_context():
    with patch("module.external_api") as mock_api:
        mock_api.return_value = "mocked result"
        result = my_function()
        assert result == "mocked result"

# Patch class/object attribute
def test_patch_attribute():
    with patch.object(MyClass, "method", return_value="mocked"):
        obj = MyClass()
        result = obj.method()
        assert result == "mocked"

8. Fixtures and Test Setup

Java (JUnit 4):

public class UserServiceTest {
    @Rule
    public final ExpectedException exception = ExpectedException.none();
    
    private UserService userService;
    private UserRepository userRepository;
    
    @Before
    public void setUp() {
        userRepository = mock(UserRepository.class);
        userService = new UserService(userRepository);
    }
    
    @Test
    public void testGetUser() {
        User user = new User("alice", "Alice");
        when(userRepository.findById(1)).thenReturn(user);
        
        User result = userService.getUser(1);
        
        assertEquals("alice", result.getUsername());
    }
}

Python (pytest fixtures):

import pytest
from unittest.mock import Mock

class UserService:
    def __init__(self, user_repository):
        self.user_repository = user_repository
    
    def get_user(self, user_id):
        return self.user_repository.find_by_id(user_id)

@pytest.fixture
def user_repository():
    return Mock()

@pytest.fixture
def user_service(user_repository):
    return UserService(user_repository)

def test_get_user(user_service, user_repository):
    user = {"id": 1, "username": "alice"}
    user_repository.find_by_id.return_value = user
    
    result = user_service.get_user(1)
    
    assert result["username"] == "alice"
    user_repository.find_by_id.assert_called_once_with(1)

9. Practical Example: Service Testing

Java:

public class PaymentService {
    private PaymentGateway gateway;
    private TransactionLogger logger;
    
    public PaymentService(PaymentGateway gateway, TransactionLogger logger) {
        this.gateway = gateway;
        this.logger = logger;
    }
    
    public boolean processPayment(double amount, String cardToken) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        
        try {
            boolean success = gateway.charge(cardToken, amount);
            logger.log("Payment processed: " + amount + " - " + (success ? "SUCCESS" : "FAILED"));
            return success;
        } catch (Exception e) {
            logger.log("Payment error: " + e.getMessage());
            return false;
        }
    }
}

public class PaymentServiceTest {
    @Mock
    private PaymentGateway gateway;
    
    @Mock
    private TransactionLogger logger;
    
    private PaymentService paymentService;
    
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        paymentService = new PaymentService(gateway, logger);
    }
    
    @Test
    public void testSuccessfulPayment() {
        when(gateway.charge("token123", 100.0)).thenReturn(true);
        
        boolean result = paymentService.processPayment(100.0, "token123");
        
        assertTrue(result);
        verify(gateway).charge("token123", 100.0);
        verify(logger).log(contains("SUCCESS"));
    }
    
    @Test
    public void testFailedPayment() {
        when(gateway.charge("token123", 100.0)).thenReturn(false);
        
        boolean result = paymentService.processPayment(100.0, "token123");
        
        assertFalse(result);
        verify(logger).log(contains("FAILED"));
    }
    
    @Test(expected = IllegalArgumentException.class)
    public void testNegativeAmount() {
        paymentService.processPayment(-50.0, "token123");
    }
}

Python:

from unittest.mock import Mock, patch, call

class PaymentService:
    def __init__(self, gateway, logger):
        self.gateway = gateway
        self.logger = logger
    
    def process_payment(self, amount, card_token):
        if amount <= 0:
            raise ValueError("Amount must be positive")
        
        try:
            success = self.gateway.charge(card_token, amount)
            status = "SUCCESS" if success else "FAILED"
            self.logger.log(f"Payment processed: {amount} - {status}")
            return success
        except Exception as e:
            self.logger.log(f"Payment error: {str(e)}")
            return False

class TestPaymentService:
    def setup_method(self):
        self.gateway = Mock()
        self.logger = Mock()
        self.payment_service = PaymentService(self.gateway, self.logger)
    
    def test_successful_payment(self):
        self.gateway.charge.return_value = True
        
        result = self.payment_service.process_payment(100.0, "token123")
        
        assert result is True
        self.gateway.charge.assert_called_once_with("token123", 100.0)
        self.logger.log.assert_called_once()
        assert "SUCCESS" in self.logger.log.call_args[0][0]
    
    def test_failed_payment(self):
        self.gateway.charge.return_value = False
        
        result = self.payment_service.process_payment(100.0, "token123")
        
        assert result is False
        assert "FAILED" in self.logger.log.call_args[0][0]
    
    def test_negative_amount(self):
        with pytest.raises(ValueError):
            self.payment_service.process_payment(-50.0, "token123")

Python (pytest version):

import pytest
from unittest.mock import Mock

def test_successful_payment():
    gateway = Mock()
    logger = Mock()
    gateway.charge.return_value = True
    
    payment_service = PaymentService(gateway, logger)
    result = payment_service.process_payment(100.0, "token123")
    
    assert result is True
    gateway.charge.assert_called_once_with("token123", 100.0)
    assert "SUCCESS" in logger.log.call_args[0][0]

def test_failed_payment():
    gateway = Mock()
    logger = Mock()
    gateway.charge.return_value = False
    
    payment_service = PaymentService(gateway, logger)
    result = payment_service.process_payment(100.0, "token123")
    
    assert result is False
    assert "FAILED" in logger.log.call_args[0][0]

def test_negative_amount():
    gateway = Mock()
    logger = Mock()
    payment_service = PaymentService(gateway, logger)
    
    with pytest.raises(ValueError):
        payment_service.process_payment(-50.0, "token123")

10. Test Coverage

Java (JaCoCo):

<!-- In pom.xml -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.8</version>
</plugin>

<!-- Run: mvn clean jacoco:report -->

Python (coverage.py):

# Install
pip install coverage

# Run tests with coverage
coverage run -m pytest

# Generate report
coverage report
coverage html  # Creates htmlcov/index.html

Summary Table

  • Test Framework

    • Java (JUnit + Mockito): JUnit
    • Python (unittest): unittest
    • Python (pytest): pytest
  • Basic Test

    • Java (JUnit + Mockito): @Test annotation
    • Python (unittest): test_ method
    • Python (pytest): test_ function
  • Setup

    • Java (JUnit + Mockito): @Before
    • Python (unittest): setUp() method
    • Python (pytest): @pytest.fixture
  • Teardown

    • Java (JUnit + Mockito): @After
    • Python (unittest): tearDown() method
    • Python (pytest): fixture cleanup
  • Assertions

    • Java (JUnit + Mockito): assertEquals(), etc.
    • Python (unittest): self.assertEqual(), etc.
    • Python (pytest): assert statement
  • Exception Testing

    • Java (JUnit + Mockito): @Test(expected=...)
    • Python (unittest): assertRaises()
    • Python (pytest): pytest.raises()
  • Mocking

    • Java (JUnit + Mockito): mock() / spy()
    • Python (unittest): Mock()
    • Python (pytest): Mock() from unittest.mock
  • Mock Setup

    • Java (JUnit + Mockito): when(...).thenReturn(...)
    • Python (unittest): .return_value =
    • Python (pytest): .return_value =
  • Verification

    • Java (JUnit + Mockito): verify()
    • Python (unittest): assert_called_with()
    • Python (pytest): assert_called_with()
  • Parametrization

    • Java (JUnit + Mockito): @Parameterized
    • Python (unittest): parameterized library
    • Python (pytest): @pytest.mark.parametrize
  • Coverage

    • Java (JUnit + Mockito): JaCoCo
    • Python (unittest): coverage.py
    • Python (pytest): coverage.py