The test suite has been successfully converted from unittest to pytest!
- Simpler syntax - No need to inherit from
TestCaseor useself.assert* - Better output - More readable failure messages showing actual vs expected values
- Fixtures - More powerful and flexible setup/teardown with
@pytest.fixture - Parametrization - Easy to run same test with different inputs
- Less boilerplate - Just write functions, not classes
- Better assertion introspection - Automatically shows values in failures
unittest (old):
class TestParticle(unittest.TestCase):
def setUp(self):
self.position = np.array([1.0, 2.0])
def test_kinetic_energy(self):
particle = Particle(position=self.position, ...)
self.assertAlmostEqual(particle.kinetic_energy, expected, places=5)pytest (new):
@pytest.fixture
def particle_params():
return {'position': np.array([1.0, 2.0])}
def test_kinetic_energy(particle_params):
particle = Particle(position=particle_params['position'], ...)
assert particle.kinetic_energy == pytest.approx(expected, rel=1e-5)Install dependencies:
pip install -r requirements.txtOr install pytest directly:
pip install pytest pytest-cov# Run all tests
pytest tests/
# Run with verbose output
pytest tests/ -v
# Run specific test file
pytest tests/test_particle.py
# Run specific test function
pytest tests/test_particle.py::test_kinetic_energy_moving_particle# Run with coverage report
pytest tests/ --cov=md_simulation --cov-report=term
# Generate HTML coverage report
pytest tests/ --cov=md_simulation --cov-report=html
# Run with detailed output
pytest tests/ -vv
# Show print statements
pytest tests/ -s
# Run only failed tests from last run
pytest tests/ --lf
# Stop at first failure
pytest tests/ -xAll 32 tests pass successfully:
=============================== test session starts ===============================
collected 32 items
tests/test_particle.py ........ [ 25%]
tests/test_potential.py ............... [ 71%]
tests/test_simulation.py ......... [100%]
=============================== 32 passed in 0.78s ================================
- 55% code coverage - Tests cover core functionality
- Main simulation code: 250 statements, 138 covered
test_particle.py:
particle_params- Common particle parameters (position, velocity, mass)
test_potential.py:
lj_potential- LennardJonesPotential instance with Argon parameterslj_params- Dictionary of Lennard-Jones parameters
test_simulation.py:
lj_potential- Lennard-Jones potential for simulationsparticles- Tuple of two particles (one moving, one fixed)simulation- Complete TwoParticleMD simulation instance
Particle Tests (8 tests):
- ✅ Initialization
- ✅ Kinetic energy (moving, fixed, stationary)
- ✅ Property mutability
Potential Tests (15 tests):
- ✅ Initialization
- ✅ Potential energy at various distances
- ✅ Force magnitude and direction
- ✅ Repulsive vs attractive regimes
- ✅ 2D force vectors
Simulation Tests (9 tests):
- ✅ Initialization
- ✅ Energy calculation and conservation
- ✅ Time stepping
- ✅ Wall collisions
- ✅ Fixed particle behavior
- ✅ History recording
Create a new test file or add to existing ones:
import pytest
import numpy as np
from md_simulation import Particle
@pytest.fixture
def my_fixture():
"""Fixture providing test data."""
return {'value': 42}
def test_something(my_fixture):
"""Test description."""
result = calculate_something(my_fixture['value'])
assert result == expected_value
def test_with_approx():
"""Test floating point comparison."""
result = 3.14159265
assert result == pytest.approx(3.14159, rel=1e-5)
def test_array_comparison():
"""Test numpy array comparison."""
result = np.array([1.0, 2.0, 3.0])
expected = np.array([1.0, 2.0, 3.0])
np.testing.assert_array_equal(result, expected)@pytest.mark.parametrize("input,expected", [
(1, 2),
(2, 4),
(3, 6),
])
def test_double(input, expected):
assert input * 2 == expected@pytest.mark.slow
def test_long_running():
# Long test
pass
# Run only slow tests: pytest -m slow
# Skip slow tests: pytest -m "not slow"def test_raises_error():
with pytest.raises(ValueError):
raise ValueError("Expected error")✅ All tests converted from unittest to pytest
✅ All 32 tests passing
✅ 55% code coverage
✅ Cleaner, more readable test code
✅ Better error messages
✅ Fixtures for reusable test setup
✅ Coverage reporting enabled